diff --git a/sdk/ai/azure-ai-agents/azure/ai/agents/_utils/model_base.py b/sdk/ai/azure-ai-agents/azure/ai/agents/_utils/model_base.py index 512d10f7c64a..7ec44978519b 100644 --- a/sdk/ai/azure-ai-agents/azure/ai/agents/_utils/model_base.py +++ b/sdk/ai/azure-ai-agents/azure/ai/agents/_utils/model_base.py @@ -816,16 +816,16 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-retur # is it optional? try: - if any(a for a in annotation.__args__ if a == type(None)): # pyright: ignore + if any(a for a in annotation.__args__ if a == type(None)): # pyright: ignore # pylint: disable=unidiomatic-typecheck if len(annotation.__args__) <= 2: # pyright: ignore if_obj_deserializer = _get_deserialize_callable_from_annotation( - next(a for a in annotation.__args__ if a != type(None)), module, rf # pyright: ignore + next(a for a in annotation.__args__ if a != type(None)), module, rf # pyright: ignore # pylint: disable=unidiomatic-typecheck ) return functools.partial(_deserialize_with_optional, if_obj_deserializer) # the type is Optional[Union[...]], we need to remove the None type from the Union annotation_copy = copy.copy(annotation) - annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a != type(None)] # pyright: ignore + annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a != type(None)] # pyright: ignore # pylint: disable=unidiomatic-typecheck return _get_deserialize_callable_from_annotation(annotation_copy, module, rf) except AttributeError: pass diff --git a/sdk/ai/azure-ai-agents/azure/ai/agents/models/_patch.py b/sdk/ai/azure-ai-agents/azure/ai/agents/models/_patch.py index 70a0891dc74b..d7e7d5aa8586 100644 --- a/sdk/ai/azure-ai-agents/azure/ai/agents/models/_patch.py +++ b/sdk/ai/azure-ai-agents/azure/ai/agents/models/_patch.py @@ -333,9 +333,9 @@ def _map_type(annotation) -> Dict[str, Any]: # pylint: disable=too-many-return- if origin is Union: args = get_args(annotation) # If Union contains None, it is an optional parameter - if type(None) in args: + if type(None) in args: # pylint: disable=unidiomatic-typecheck # If Union contains only one non-None type, it is a nullable parameter - non_none_args = [arg for arg in args if arg is not type(None)] + non_none_args = [arg for arg in args if arg is not type(None)] # pylint: disable=unidiomatic-typecheck if len(non_none_args) == 1: schema = _map_type(non_none_args[0]) if "type" in schema: diff --git a/sdk/ai/azure-ai-projects/.env.template b/sdk/ai/azure-ai-projects/.env.template index 0ca16cf1517e..76c795cfaf62 100644 --- a/sdk/ai/azure-ai-projects/.env.template +++ b/sdk/ai/azure-ai-projects/.env.template @@ -57,6 +57,9 @@ A2A_USER_INPUT= AZURE_TEST_RUN_LIVE=false AZURE_SKIP_LIVE_RECORDING=true +#Used by hosted agent +FOUNDRY_HOSTED_AGENT_NAME= + # Used in Fine-tuning tests COMPLETED_OAI_MODEL_SFT_FINE_TUNING_JOB_ID= COMPLETED_OAI_MODEL_RFT_FINE_TUNING_JOB_ID= @@ -88,6 +91,6 @@ RUN_EXTENDED_FINE_TUNING_LIVE_TESTS=false # - SAMPLE_TEST_ERROR_LOG: Sample crashed with an exception during execution # - SAMPLE_TEST_FAILED_LOG: Sample ran successfully but LLM validation failed (incorrect output) # - SAMPLE_TEST_PASSED_LOG: Sample ran successfully and LLM validation passed (correct output) -# SAMPLE_TEST_PASSED_LOG=_success_.log -# SAMPLE_TEST_FAILED_LOG=_failed_.log -# SAMPLE_TEST_ERROR_LOG=_errors_.log +SAMPLE_TEST_PASSED_LOG=_success_.log +SAMPLE_TEST_FAILED_LOG=_failed_.log +SAMPLE_TEST_ERROR_LOG=_errors_.log diff --git a/sdk/ai/azure-ai-projects/.github/skills/README.md b/sdk/ai/azure-ai-projects/.github/skills/README.md new file mode 100644 index 000000000000..7ea120484d10 --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/README.md @@ -0,0 +1,33 @@ +# CoPilot skills for azure-ai-projects development + +## Prerequisite + +* Clone the `azure-sdk-for-python` repo to your local machine, if you don't already have it: + ``` + 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`. +* 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 + +### Using GitHub CoPilot in VSCode + +* 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. +* 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. +* Answer some questions and approve execution to go through the workflow + + + + + 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 new file mode 100644 index 000000000000..190df28bcbd0 --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-emit-from-typespec/SKILL.md @@ -0,0 +1,234 @@ +--- +name: azure-ai-projects-emit-from-typespec +license: MIT +metadata: + version: "1.0.0" + distribution: local +description: "Emit the azure-ai-projects Python SDK from TypeSpec, apply post-emitter fixes, update changelog, and create a Pull Request. WHEN: \"emit SDK from TypeSpec\", \"generate azure-ai-projects SDK\", \"update azure-ai-projects from TypeSpec\", \"emit from TypeSpec\", \"regenerate azure-ai-projects\". DO NOT USE FOR: other Azure SDK packages, manual code edits without TypeSpec. INVOKES: azsdk-common-generate-sdk-locally skill, post-emitter-fixes.cmd script, git commands, gh CLI for PR creation." +compatibility: + requires: "azure-sdk-mcp server, local azure-sdk-for-python clone, git, gh CLI" +--- + +# Emit azure-ai-projects Python SDK from TypeSpec + +This skill guides Copilot through emitting the azure-ai-projects Python SDK from TypeSpec, +applying post-emitter fixes, updating the changelog, installing package from sources and creating a Pull Request. + +**Working directory:** `sdk/ai/azure-ai-projects` + +**Skills:** This workflow relies on skills defined under `.github/skills/` at the root of the repository. Use those skills for SDK generation, building, changelog updates, and other SDK lifecycle operations instead of running commands directly. In particular: + +- **`azsdk-common-generate-sdk-locally`** – For generating SDK from TypeSpec, building, running checks/tests, updating changelog, metadata, and version. + +--- + +## Step 1: Gather information from the user + +Ask the user the following questions **one at a time**, waiting for each answer before proceeding. + +### 1a. Topic branch name + +Ask the user to choose **one** of the following two options for the target topic branch: + +1. **Create a new topic branch (with default branch name)** – Create a new topic branch for the emitted changes. If selected, this default branch name will be used "/", where `github-userid` is the user's GitHub ID and `DD-MM-HHMM` is the current date-time using date, month, hour and minute. For example, if the GitHub ID is "dargilco" and the current date and time is May 1st, 2026 at 8:13am, the default branch name would be `dargilco/emit-from-typespec-01-05-0813`. This should be the default option, and the default branch name should be displayed. If you press enter without typing anything, this option will be selected. + +2. **Create a new topic branch (branch name given by user)** - Ask the user for the branch name. Mention that a common format is "/". If the user enters a branch name `feature/azure-ai-projects/2.2.0` then stop and report that they cannot emit directly to the current feature branch. + +3. **Emit to current branch** – Emit directly to the current branch without creating a new topic branch. This is not common, but may be necessary if the user is re-running this workflow because of a previous failure, where the topic branch was already created. If the current branch is named `feature/azure-ai-projects/2.2.0` then stop and report that they cannot emit directly to the current feature branch. + +### 1b. TypeSpec source + +Ask the user to choose **one** of the following three options for the TypeSpec source: + +1. **Latest commit on `feature/foundry-release`** – Automatically find the latest commit to the `feature/foundry-release` branch in [Azure/azure-rest-api-specs](https://github.com/Azure/azure-rest-api-specs) that touched files under `specification/ai-foundry/data-plane/Foundry`, and use that commit hash. This should be the default option. If you press enter without typing anything, this option will be selected. + +2. **Local TypeSpec folder** – Emit from a local clone of the [azure-rest-api-specs](https://github.com/Azure/azure-rest-api-specs) repository. If selected, ask for the **full folder path** to the TypeSpec project. This is the folder ending with `\specification\ai-foundry\data-plane\Foundry`. If it does not end with that string, stop and report the error to the user. Do not continue. + +3. **TypeSpec commit hash** – Emit from a specific commit in the [azure-rest-api-specs](https://github.com/Azure/azure-rest-api-specs) repository. If selected, ask for the **full commit SHA** (40 characters). + + +--- + +## Step 2: Record the current branch + +Before creating the topic branch, record the name of the **current Git branch**. This is the branch that the topic branch will be created from, and the branch the PR will target. + +``` +git branch --show-current +``` + +Save this as `BASE_BRANCH`. + +--- + +## Step 3: Create the topic branch + +Create the topic branch off the current branch and switch to it: + +``` +git fetch +git switch -c origin/ +``` + +Replace `` with the name provided by the user in Step 1a. + +--- + +## Step 4: Emit SDK from TypeSpec + +Use the **`azsdk-common-generate-sdk-locally`** skill to generate the SDK code. The skill knows how to invoke `azsdk_package_generate_code` and related MCP tools. + +Provide the skill with the TypeSpec source selected by the user. With is either: + +- **Local folder:** Pass the local spec repo path for local generation. Or, +- **Commit hash:** Update `commit:` in `tsp-location.yaml` to the full SHA first, then invoke the skill for generation. + +Note: +- You are only allowed to use the `tsp-client update` command. Do not use any of the other `tsp-client` commands. +- If you are generating from local TypeSpec folder, do not edit the file `tsp-location.yaml`. Leave it as is. It should not be used by the emitter. +- If you are generating from local TypeSpec folder, make sure that the local folder path you provide `tsp-client update --local-spec-repo` ends with `specification\ai-foundry\data-plane\Foundry`. +- **If the generation fails**, stop and report the error to the user. Do not continue. + +--- + +## Step 5: Revert changes to file pyproject.toml + +After the emit, there will be changes to `pyproject.toml` that are not needed. Revert any changes to `pyproject.toml` by running: + +``` +git restore pyproject.toml +``` + +--- + +## Step 6: Commit and push + +Stage all changes (excluding file names that start with `.env`), commit, and push the topic branch: + +``` +git add -A -- ':!.env*' +git commit -m "Part 1: Emit SDK from TypeSpec" + +Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>" +git push -u origin +``` + +--- + +## Step 7: Run post-emitter fixes + +After a successful emit, run the post-emitter fix script located in the `sdk/ai/azure-ai-projects` folder: + +``` +post-emitter-fixes.cmd +``` + +This script applies azure-ai-projects-specific corrections to the emitted code (restores `pyproject.toml`, fixes enum names, patches Sphinx doc-string issues, and runs `black` formatting). + +**If the script fails**, stop and report the error to the user. Do not continue. Do not attempt to analyze the script failures and fix them with Copilot. The script should be fixed by the engineering team if it is not working. + +--- + +## Step 8: Commit and push + +Stage all changes (excluding file names that start with `.env`), commit, and push the topic branch: + +``` +git add -A -- ':!.env*' +git commit -m "Part 2: Apply post-emitter-fixes.cmd" + +Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>" +git push -u origin +``` + +--- + +## Step 9: Fix patched code related to preview feature headers + +The emitted code may have introduced another beta sub-client (a new property on class `BetaOperations`). It may have also added another enum value to the existing internal class `_FoundryFeaturesOptInKeys`. This means that the client library needs to set a new HTTP request header when making REST API calls to the service, to opt-in to the new service features which are still in preview. If that's the case, do the following: + +* Update the dictionary `_BETA_OPERATION_FEATURE_HEADERS` defined in `azure\ai\projects\models\_patch.py`, to include a new key-value pair to map the new beta sub-client name to the proper value from `_FoundryFeaturesOptInKeys`. If no new beta sub-client was introduced, but a new enum value was added to `_FoundryFeaturesOptInKeys`, you will need to update one of the existing key-value pairs in `_BETA_OPERATION_FEATURE_HEADERS` to a comma-separated join of multiple values from `_FoundryFeaturesOptInKeys`. + +* Do a similar change to the dictionary `EXPECTED_FOUNDRY_FEATURES` defined in the test file `tests\foundry_features_header\foundry_features_header_test_base.py`: add a new key-value pair if a new beta sub-client was introduced, or update an existing key-value pair to include the new enum value if no new beta sub-client was introduced. + +* Finally, look at the two files `azure\ai\projects\operations\_patch.py` and `azure\ai\projects\aio\operations\_patch.py`. They define the public `BetaOperations` classes for the sync and async clients. To support a new sub-client, you will need to add a new property to this class with the proper doc string. You will need to update the import statement at the top of the file to import the new sub-client class. And you will need to update `__all__` statement at the bottom of the file to include the new sub-client class name. Follow the examples you see there for `BetaDatasetsOperations` or `BetaSkillsOperations`. + +If a new enum value was added to `_AgentDefinitionOptInKeys`, please print a note on screen that mentions which value was added, and tell the user that a review is needed to make sure this new value is properly used. But otherwise continue on. + +Important: Under the `azure\ai\projects` folder, you are only allowed to edit Python source files that start with "_patch". If you see that changes are needed in other files, stop and report this to the user instead of making the changes yourself. + +--- + +## Step 10: Update samples and tests + +If there were any breaking changes in existing APIs, like class or method renames: +* update the patched code accordingly in the client library to reflect those changes. Changes should be made to Python source file names that start with "_patch", under the `azure\ai\projects` folder. +* update the samples accordingly to reflect those changes. Changes should be made under `sdk/ai/azure-ai-projects/samples` folder. +* update the tests accordingly to reflect those changes. Changes should be made under `sdk/ai/azure-ai-projects/tests` folder. + +--- + +## Step 11: Install package from sources + +In the folder `sdk\ai\azure-ai-projects`, run `pip install -e .` to install the package from sources. If there are any errors, stop and report the error to the user. Do not continue. + +--- + +## 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 + +Stage all changes (excluding file names that start with `.env`), commit, and push the topic branch: + +``` +git add -A -- ':!.env*' +git commit -m "Part 3: Additional edits" + +Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>" +git push -u origin +``` + +--- + +## Step 14: Create a Pull Request + +Create a draft PR from the **topic branch** to the **base branch** (recorded in Step 2): + +``` +gh pr create --draft --base --head --assignee @me --title "" --body "" +``` + +- **Title:** Use a descriptive title such as `[azure-ai-projects] Emit SDK from TypeSpec ()`. +- **Body:** Include which TypeSpec source was used and a summary of the changelog entry. + +You must show the user the resulting PR URL on screen when done, before you continue to the next step. + +Open a new tab in the default browser and navigate to the PR URL. + +--- + +## Step 15: 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)" + +If the user answers "yes", run all tests from recordings. Follow these guidelines: +* Run tests in a local Python virtual environment. Create this virtual environment if it does not already exists: + ``` + python -m venv .venv + ``` + and activate it: + ``` + .venv\Scripts\activate + ``` +* Show test progress on screen, as tests are run. + + diff --git a/sdk/ai/azure-ai-projects/.github/skills/update-env-vars/.gitignore b/sdk/ai/azure-ai-projects/.github/skills/update-env-vars/.gitignore new file mode 100644 index 000000000000..c96a04f008ee --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/update-env-vars/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/sdk/ai/azure-ai-projects/CHANGELOG.md b/sdk/ai/azure-ai-projects/CHANGELOG.md index d6a0253d9f0a..c67809ff4764 100644 --- a/sdk/ai/azure-ai-projects/CHANGELOG.md +++ b/sdk/ai/azure-ai-projects/CHANGELOG.md @@ -1,9 +1,67 @@ # Release History +## 2.2.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 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 `.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: `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.agents` for code-based hosted agents: `create_version_from_code`, `download_code`. +* New methods on `.beta.agents` for optimization jobs: `create_optimization_job`, `get_optimization_job`, `list_optimization_jobs`, `cancel_optimization_job`, `list_optimization_candidates`. +* New methods on `.beta.memory_stores` to handle individual memory items:`.beta.memory_stores`: `get_memory`, `delete_memory`. +* 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. +* 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. +* New convenience method `.beta.models.create()` that wraps the spec's three-step upload-first sequence (`pending_upload` → `azcopy copy` → `pending_create_version`) and polls `get()` until the new `ModelVersion` is observable. + +### Breaking Changes + +Breaking changes in beta methods: +* Required keyword `isolation_key` removed from `.beta.agents.create_session()` and `.beta.agents.delete_session()` methods. +* Argument `body` in methods `.beta.evaluation_taxonomies.create()` and `.beta.evaluation_taxonomies.update()` renamed to `taxonomy`. +* Argument `body` in method `.beta.skills.create_from_package()` renamed to `content`. + +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`. +* Renamed class `Target` to `EvaluationTarget`. +* Renamed class `TargetConfig` to `RedTeamTargetConfig`. +* Removed class `FabricIQPreviewToolParameters`. +* Removed class `WorkIQPreviewToolParameters`. + +### 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 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`. + ## 2.1.0 (2026-04-20) ### Features Added +* New `WorkIQPreviewTool`. * `get_openai_client()` on `AIProjectClient` now takes an optional input argument `agent_name`. If provided, the returned OpenAI client will use a base URL of Agent endpoint instead of Foundry Project endpoint. As Agent endpoints are a preview feature, you need to set `allow_preview=True` on the `AIProjectClient` constructor. diff --git a/sdk/ai/azure-ai-projects/README.md b/sdk/ai/azure-ai-projects/README.md index b2f754b91fd2..44365a930a5f 100644 --- a/sdk/ai/azure-ai-projects/README.md +++ b/sdk/ai/azure-ai-projects/README.md @@ -1,6 +1,6 @@ # Azure AI Projects client library for Python -The AI Projects client library (in preview) is part of the Microsoft Foundry SDK, and provides easy access to +The AI Projects client library is part of the Microsoft Foundry SDK, and provides easy access to resources in your Microsoft Foundry Project. Use it to: * **Create and run Agents** using methods on the `.agents` client property. @@ -13,6 +13,7 @@ resources in your Microsoft Foundry Project. Use it to: * Browser Automation (Preview) * Code Interpreter * Computer Use (Preview) + * Fabric IQ (Preview) * File Search * Function Tool * Image Generation @@ -21,8 +22,10 @@ resources in your Microsoft Foundry Project. Use it to: * Microsoft SharePoint (Preview) * Model Context Protocol (MCP) * OpenAPI + * Toolbox Search (Preview) * Web Search * Web Search (Preview) + * Work IQ (Preview) * **Get an OpenAI client** using `.get_openai_client()` method to run Responses, Conversations, Evaluations and Fine-Tuning operations with your Agent. * **Manage memory stores (preview)** for Agent conversations, using `.beta.memory_stores` operations. * **Explore additional evaluation tools (some in preview)** to assess the performance of your generative AI application, using `.evaluation_rules`, @@ -32,6 +35,7 @@ resources in your Microsoft Foundry Project. Use it to: * **Enumerate AI Models** deployed to your Foundry Project using `.deployments` operations. * **Enumerate connected Azure resources** in your Foundry project using `.connections` operations. * **Upload documents and create Datasets** to reference them using `.datasets` operations. +* **Register and manage local model weights** as Foundry `ModelVersion` resources using `.beta.models` operations, including the `create` end-to-end helper. * **Create and enumerate Search Indexes** using `.indexes` operations. The client library uses version `v1` of the Microsoft Foundry [data plane REST APIs](https://aka.ms/azsdk/azure-ai-projects-v2/api-reference-v1). @@ -55,8 +59,7 @@ To report an issue with the client library, or request additional features, plea * An [Azure subscription][azure_sub]. * A [project in Microsoft Foundry](https://learn.microsoft.com/azure/foundry/how-to/create-projects). * A Foundry project endpoint URL of the form `https://your-ai-services-account-name.services.ai.azure.com/api/projects/your-project-name`. It can be found in your Microsoft Foundry Project home page. Below we will assume the environment variable `FOUNDRY_PROJECT_ENDPOINT` was defined to hold this value. -* To authenticate using API key, you will need the "Project API key" as shown in your Microsoft Foundry Project home page. -* To authenticate using Entra ID, your application needs an object that implements the [TokenCredential](https://learn.microsoft.com/python/api/azure-core/azure.core.credentials.tokencredential) interface. Code samples here use [DefaultAzureCredential](https://learn.microsoft.com/python/api/azure-identity/azure.identity.defaultazurecredential). To get that working, you will need: +* Client authentication is done using Entra ID. To authenticate, your application needs an object that implements the [TokenCredential](https://learn.microsoft.com/python/api/azure-core/azure.core.credentials.tokencredential) interface. Code samples here use [DefaultAzureCredential](https://learn.microsoft.com/python/api/azure-identity/azure.identity.defaultazurecredential). To get that working, you will need: * An appropriate role assignment. See [Role-based access control in Microsoft Foundry portal](https://learn.microsoft.com/azure/foundry/concepts/rbac-foundry). Role assignment can be done via the "Access Control (IAM)" tab of your Azure AI Project resource in the Azure portal. * [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) installed. * You are logged into your Azure account by running `az login`. @@ -116,12 +119,12 @@ async with ( For comprehensive examples covering Agents, tool usage, evaluation, fine-tuning, datasets, indexes, and more, see: -* **[Microsoft Foundry Agents overview](https://learn.microsoft.com/azure/foundry/agents/overview)** — concepts, setup, and quickstarts. +* **[Microsoft Foundry Agents overview](https://learn.microsoft.com/azure/foundry/agents/overview)** — concepts, setup, and quick-starts. * **[Runtime components](https://learn.microsoft.com/azure/foundry/agents/concepts/runtime-components?tabs=python)** — deep-dive into agent architecture. * **[Tool catalog](https://learn.microsoft.com/azure/foundry/agents/concepts/tool-catalog)** — all available tools and agent capabilities. * **[SDK samples folder][samples]** — fully runnable Python code for synchronous and asynchronous clients covering all operations below. -The sections below cover SDK-specific behaviours (authentication variants, exception handling, logging, tracing) that are not documented in the above Learn pages. +The sections below cover SDK-specific behaviors (authentication variants, exception handling, logging, tracing) that are not documented in the above Learn pages. ### Performing Responses operations using OpenAI client @@ -164,6 +167,7 @@ Full descriptions and working code for all of the above are available in: | Deployments | [Deployment types](https://learn.microsoft.com/azure/foundry/foundry-models/concepts/deployment-types) | `samples/deployments/` | | Connections | [Connections operations](https://learn.microsoft.com/python/api/overview/azure/ai-projects-readme?view=azure-python#connections-operations) | `samples/connections/` | | Datasets | [Dataset operations](https://learn.microsoft.com/python/api/overview/azure/ai-projects-readme?view=azure-python#dataset-operations) | `samples/datasets/` | +| Models (preview) | Register local model weights as Foundry `ModelVersion` resources via `.beta.models` (`create`, `list`, `list_versions`, `get`, `update`, `delete`, `pending_upload`, `pending_create_version`, `get_credentials`). | `samples/models/` | | Indexes | [Azure AI Search](https://learn.microsoft.com/azure/search/search-what-is-azure-search) | `samples/indexes/` | | Files (upload, retrieve, list, delete) | [OpenAI Files API](https://platform.openai.com/docs/api-reference/files) | `samples/files/` | | Fine-tuning | [Fine-Tuning in AI Foundry](https://github.com/microsoft-foundry/fine-tuning) | `samples/finetuning/` | @@ -179,219 +183,13 @@ For product guidance, see: For SDK usage examples in this package, see `samples/hosted_agents/`, including CRUD, file upload/download, and skills scenarios. -## Tracing +## Client-side tracing -### Experimental Feature Gate +See [Add client-side tracing to Foundry agents (preview)](https://learn.microsoft.com/azure/foundry/observability/how-to/trace-agent-client-side?tabs=python). -**Important:** GenAI tracing instrumentation is an experimental preview feature. Spans, attributes, and events may be modified in future versions. To use it, you must explicitly opt in by setting the environment variable: +**Important:** GenAI tracing instrumentation is an experimental preview feature. Spans, attributes, and events may be modified in future versions. -```bash -AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING=true -``` - -This environment variable must be set before calling `AIProjectInstrumentor().instrument()`. If the environment variable is not set or is set to any value other than `true` (case-insensitive), tracing instrumentation will not be enabled and a warning will be logged. - -Only enable this feature after reviewing your requirements and understanding that the tracing behavior may change in future versions. - -### Getting Started with Tracing - -You can add an Application Insights Azure resource to your Microsoft Foundry project. See the Tracing tab in your Microsoft Foundry project. If one was enabled, you can get the Application Insights connection string, configure your AI Projects client, and observe traces in Azure Monitor. Typically, you might want to start tracing before you create a client or Agent. - -For tracing concepts in Microsoft Foundry, see [Trace an agent](https://learn.microsoft.com/azure/foundry/observability/concepts/trace-agent-concept). - -### Installation - -Make sure to install OpenTelemetry and the Azure SDK tracing plugin via - -```bash -pip install "azure-ai-projects>=2.0.0b4" opentelemetry-sdk azure-core-tracing-opentelemetry azure-monitor-opentelemetry -``` - -You will also need an exporter to send telemetry to your observability backend. You can print traces to the console or use a local viewer such as [Aspire Dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone?tabs=bash). - -To connect to Aspire Dashboard or another OpenTelemetry compatible backend, install OTLP exporter: - -```bash -pip install opentelemetry-exporter-otlp -``` - -### How to enable tracing - -**Remember:** Before enabling tracing, ensure you have set the `AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING=true` environment variable as described in the [Experimental Feature Gate](#experimental-feature-gate) section. - -Here is a code sample that shows how to enable Azure Monitor tracing: - - - -```python -# Enable Azure Monitor tracing -application_insights_connection_string = project_client.telemetry.get_application_insights_connection_string() -configure_azure_monitor(connection_string=application_insights_connection_string) -``` - - - -You may also want to create a span for your scenario: - - - -```python -tracer = trace.get_tracer(__name__) -scenario = os.path.basename(__file__) - -with tracer.start_as_current_span(scenario): -``` - - - -See the full sample in file `\agents\telemetry\sample_agent_basic_with_azure_monitor_tracing.py` in the [Samples][samples] folder. - -**Note:** In order to view the traces in the Microsoft Foundry portal, the agent ID should be passed in as part of the response generation request. - -In addition, you might find it helpful to see the tracing logs in the console. Remember to set `AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING=true` before running the following code: - - - -```python -# Setup tracing to console -# Requires opentelemetry-sdk -span_exporter = ConsoleSpanExporter() -tracer_provider = TracerProvider() -tracer_provider.add_span_processor(SimpleSpanProcessor(span_exporter)) -trace.set_tracer_provider(tracer_provider) -tracer = trace.get_tracer(__name__) - -# Enable instrumentation with content tracing -AIProjectInstrumentor().instrument() -``` - - - -See the full sample in file `\agents\telemetry\sample_agent_basic_with_console_tracing.py` in the [Samples][samples] folder. - -### Enabling trace context propagation - -Trace context propagation allows client-side spans generated by the Projects SDK to be correlated with server-side spans from Azure OpenAI and other Azure services. When enabled, the SDK automatically injects W3C Trace Context headers (`traceparent` and `tracestate`) into HTTP requests made by OpenAI clients obtained via `get_openai_client()`. - -This feature ensures that all operations within a distributed trace share the same trace ID, providing end-to-end visibility across your application and Azure services in your observability backend (such as Azure Monitor). - -Trace context propagation is **enabled by default** when tracing is enabled (for example through `configure_azure_monitor` or the `AIProjectInstrumentor().instrument()` call). To disable it, set the `AZURE_TRACING_GEN_AI_ENABLE_TRACE_CONTEXT_PROPAGATION` environment variable to `false`, or pass `enable_trace_context_propagation=False` to the `AIProjectInstrumentor().instrument()` call. - -**When does the change take effect?** -- Changes to `enable_trace_context_propagation` (whether via `instrument()` or the environment variable) only affect OpenAI clients obtained via `get_openai_client()` **after** the change is applied. Previously acquired clients are unaffected. -- To apply the new setting to all clients, call `AIProjectInstrumentor().instrument(enable_trace_context_propagation=)` before acquiring your OpenAI clients, or re-acquire the clients after making the change. - -**Security and Privacy Considerations:** -- **Trace IDs are sent to external services**: The `traceparent` and `tracestate` headers from your client-side originating spans are injected into requests sent to service. This enables end-to-end distributed tracing, but note that the trace identifier may be shared beyond the initial API call. -- **Enabled by Default**: If you have privacy or compliance requirements that prohibit sharing trace identifiers with services, disable trace context propagation by setting `enable_trace_context_propagation=False` or the environment variable to `false`. - -#### Controlling baggage propagation - -When trace context propagation is enabled, you can separately control whether the baggage header is included. By default, only `traceparent` and `tracestate` headers are propagated. To also include the `baggage` header, set the `AZURE_TRACING_GEN_AI_TRACE_CONTEXT_PROPAGATION_INCLUDE_BAGGAGE` environment variable to `true`: - -If no value is provided for the `enable_baggage_propagation` parameter with the `AIProjectInstrumentor.instrument()` call and the environment variable is not set, the value defaults to `false` and baggage is not included. - -**Note:** The `enable_baggage_propagation` flag is evaluated dynamically on each request, so changes take effect **immediately** for all clients that have the trace context propagation hook registered. However, the hook is only registered on clients acquired via `get_openai_client()` **while trace context propagation was enabled**. Clients acquired when trace context propagation was disabled will never propagate baggage, regardless of the `enable_baggage_propagation` value. - -**Why is baggage propagation separate?** - -The baggage header can contain arbitrary key-value pairs added anywhere in your application's trace context. Unlike trace IDs (which are randomly generated identifiers), baggage may contain: - -- User identifiers or session information -- Authentication tokens or credentials -- Business-specific data or metadata -- Personally identifiable information (PII) - -Baggage is automatically propagated through your entire application's call chain, meaning data added in one part of your application will be included in requests to Azure OpenAI unless explicitly controlled. - -**Important Security Considerations:** - -- **Review Baggage Contents**: Before enabling baggage propagation, audit what data your application (and any third-party libraries) adds to OpenTelemetry baggage. -- **Sensitive Data Risk**: Baggage is sent to Azure OpenAI and may be logged or processed by Microsoft services. Never add sensitive information to baggage when baggage propagation is enabled. -- **Opt-in by Design**: Baggage propagation is disabled by default (even when trace context propagation is enabled) to prevent accidental exposure of sensitive data. -- **Minimal Propagation**: `traceparent` and `tracestate` headers are generally sufficient for distributed tracing. Only enable baggage propagation if your specific observability requirements demand it. - -### Enabling content recording - -Content recording controls whether message contents and tool call related details, such as parameters and return values, are captured with the traces. This data may include sensitive user information. - -To enable content recording, set the `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` environment variable to `true`. If the environment variable is not set and no value is provided with the `AIProjectInstrumentor().instrument()` call for the content recording parameter, content recording defaults to `false`. - -**Important:** The environment variable only controls content recording for built-in traces. When you use custom tracing decorators on your own functions, all parameters and return values are always traced. - -### Disabling automatic instrumentation - -The AI Projects client library automatically instruments OpenAI responses and conversations operations through `AiProjectInstrumentation`. You can disable this instrumentation by setting the environment variable `AZURE_TRACING_GEN_AI_INSTRUMENT_RESPONSES_API` to `false`. If the environment variable is not set, the responses and conversations APIs will be instrumented by default. - -### Tracing Binary Data - -Binary data are images and files sent to the service as input messages. When you enable content recording (`OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` set to `true`), by default you only trace file IDs and filenames. To enable full binary data tracing, set `AZURE_TRACING_GEN_AI_INCLUDE_BINARY_DATA` to `true`. In this case: - -* **Images**: Image URLs (including data URIs with base64-encoded content) are included -* **Files**: File data is included if sent via the API - -**Important:** Binary data can contain sensitive information and may significantly increase trace size. Some trace backends and tracing implementations may have limitations on the maximum size of trace data that can be sent to and/or supported by the backend. Ensure your observability backend and tracing implementation support the expected trace payload sizes when enabling binary data tracing. - -### How to trace your own functions - -The decorator `trace_function` is provided for tracing your own function calls using OpenTelemetry. By default the function name is used as the name for the span. Alternatively you can provide the name for the span as a parameter to the decorator. - -**Note:** The `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` environment variable does not affect custom function tracing. When you use the `trace_function` decorator, all parameters and return values are always traced by default. - -This decorator handles various data types for function parameters and return values, and records them as attributes in the trace span. The supported data types include: - -* Basic data types: str, int, float, bool -* Collections: list, dict, tuple, set - * Special handling for collections: - * If a collection (list, dict, tuple, set) contains nested collections, the entire collection is converted to a string before being recorded as an attribute. - * Sets and dictionaries are always converted to strings to ensure compatibility with span attributes. - -Object types are omitted, and the corresponding parameter is not traced. - -The parameters are recorded in attributes `code.function.parameter.` and the return value is recorder in attribute `code.function.return.value` - -#### Adding custom attributes to spans - -You can add custom attributes to spans by creating a custom span processor. Here's how to define one: - - - -```python -class CustomAttributeSpanProcessor(SpanProcessor): - def __init__(self) -> None: - pass - - def on_start(self, span: Span, parent_context=None): - # Add this attribute to all spans - span.set_attribute("trace_sample.sessionid", "123") - - # Add another attribute only to create_thread spans - if span.name == "create_thread": - span.set_attribute("trace_sample.create_thread.context", "abc") - - def on_end(self, span: ReadableSpan): - # Clean-up logic can be added here if necessary - pass -``` - - - -Then add the custom span processor to the global tracer provider: - - - -```python -provider = cast(TracerProvider, trace.get_tracer_provider()) -provider.add_span_processor(CustomAttributeSpanProcessor()) -``` - - - -See the full sample in file `\agents\telemetry\sample_agent_basic_with_console_tracing_custom_attributes.py` in the [Samples][samples] folder. - -### Additional resources - -For more information see [Agent tracing overview (preview)](https://learn.microsoft.com/azure/foundry/observability/concepts/trace-agent-concept). +Samples can be found in the sub-folders `agents/telemetry` and `telemetry` in the [Samples][samples] folder. ## Troubleshooting diff --git a/sdk/ai/azure-ai-projects/apiview-properties.json b/sdk/ai/azure-ai-projects/apiview-properties.json index f6caeb745b80..fc9d1a413d22 100644 --- a/sdk/ai/azure-ai-projects/apiview-properties.json +++ b/sdk/ai/azure-ai-projects/apiview-properties.json @@ -10,15 +10,21 @@ "azure.ai.projects.models.AgentClusterInsightRequest": "Azure.AI.Projects.AgentClusterInsightRequest", "azure.ai.projects.models.InsightResult": "Azure.AI.Projects.InsightResult", "azure.ai.projects.models.AgentClusterInsightResult": "Azure.AI.Projects.AgentClusterInsightResult", + "azure.ai.projects.models.DataGenerationJobSource": "Azure.AI.Projects.DataGenerationJobSource", + "azure.ai.projects.models.AgentDataGenerationJobSource": "Azure.AI.Projects.AgentDataGenerationJobSource", "azure.ai.projects.models.AgentDefinition": "Azure.AI.Projects.AgentDefinition", "azure.ai.projects.models.AgentDetails": "Azure.AI.Projects.AgentObject", - "azure.ai.projects.models.AgentEndpoint": "Azure.AI.Projects.AgentEndpoint", "azure.ai.projects.models.AgentEndpointAuthorizationScheme": "Azure.AI.Projects.AgentEndpointAuthorizationScheme", + "azure.ai.projects.models.AgentEndpointConfig": "Azure.AI.Projects.AgentEndpointConfig", + "azure.ai.projects.models.EvaluatorGenerationJobSource": "Azure.AI.Projects.EvaluatorGenerationJobSource", + "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", @@ -28,8 +34,9 @@ "azure.ai.projects.models.ApiKeyCredentials": "Azure.AI.Projects.ApiKeyCredentials", "azure.ai.projects.models.ApplyPatchToolParam": "OpenAI.ApplyPatchToolParam", "azure.ai.projects.models.ApproximateLocation": "OpenAI.ApproximateLocation", + "azure.ai.projects.models.ArtifactProfile": "Azure.AI.Projects.ArtifactProfile", "azure.ai.projects.models.AutoCodeInterpreterToolParam": "OpenAI.AutoCodeInterpreterToolParam", - "azure.ai.projects.models.Target": "Azure.AI.Projects.Target", + "azure.ai.projects.models.EvaluationTarget": "Azure.AI.Projects.Target", "azure.ai.projects.models.AzureAIAgentTarget": "Azure.AI.Projects.AzureAIAgentTarget", "azure.ai.projects.models.AzureAIModelTarget": "Azure.AI.Projects.AzureAIModelTarget", "azure.ai.projects.models.Index": "Azure.AI.Projects.Index", @@ -41,7 +48,7 @@ "azure.ai.projects.models.AzureFunctionDefinitionFunction": "Azure.AI.Projects.AzureFunctionDefinition.function.anonymous", "azure.ai.projects.models.AzureFunctionStorageQueue": "Azure.AI.Projects.AzureFunctionStorageQueue", "azure.ai.projects.models.AzureFunctionTool": "Azure.AI.Projects.AzureFunctionTool", - "azure.ai.projects.models.TargetConfig": "Azure.AI.Projects.TargetConfig", + "azure.ai.projects.models.RedTeamTargetConfig": "Azure.AI.Projects.RedTeamTargetConfig", "azure.ai.projects.models.AzureOpenAIModelConfiguration": "Azure.AI.Projects.AzureOpenAIModelConfiguration", "azure.ai.projects.models.BingCustomSearchConfiguration": "Azure.AI.Projects.BingCustomSearchConfiguration", "azure.ai.projects.models.BingCustomSearchPreviewTool": "Azure.AI.Projects.BingCustomSearchPreviewTool", @@ -56,6 +63,10 @@ "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", @@ -81,22 +92,45 @@ "azure.ai.projects.models.EvaluationRuleAction": "Azure.AI.Projects.EvaluationRuleAction", "azure.ai.projects.models.ContinuousEvaluationRuleAction": "Azure.AI.Projects.ContinuousEvaluationRuleAction", "azure.ai.projects.models.CosmosDBIndex": "Azure.AI.Projects.CosmosDBIndex", + "azure.ai.projects.models.CreateAgentVersionFromCodeContent": "Azure.AI.Projects.CreateAgentVersionFromCodeContent", + "azure.ai.projects.models.CreateAgentVersionFromCodeMetadata": "Azure.AI.Projects.CreateAgentVersionFromCodeMetadata", + "azure.ai.projects.models.CreateAsyncResponse": "Azure.AI.Projects.createAsync.Response.anonymous", + "azure.ai.projects.models.CreateSkillVersionFromFilesBody": "Azure.AI.Projects.CreateSkillVersionFromFilesBody", "azure.ai.projects.models.Trigger": "Azure.AI.Projects.Trigger", "azure.ai.projects.models.CronTrigger": "Azure.AI.Projects.CronTrigger", "azure.ai.projects.models.CustomCredential": "Azure.AI.Projects.CustomCredential", "azure.ai.projects.models.CustomToolParamFormat": "OpenAI.CustomToolParamFormat", "azure.ai.projects.models.CustomGrammarFormatParam": "OpenAI.CustomGrammarFormatParam", + "azure.ai.projects.models.RoutineTrigger": "Azure.AI.Projects.RoutineTrigger", + "azure.ai.projects.models.CustomRoutineTrigger": "Azure.AI.Projects.CustomRoutineTrigger", "azure.ai.projects.models.CustomTextFormatParam": "OpenAI.CustomTextFormatParam", "azure.ai.projects.models.CustomToolParam": "OpenAI.CustomToolParam", "azure.ai.projects.models.RecurrenceSchedule": "Azure.AI.Projects.RecurrenceSchedule", "azure.ai.projects.models.DailyRecurrenceSchedule": "Azure.AI.Projects.DailyRecurrenceSchedule", + "azure.ai.projects.models.DataGenerationJob": "Azure.AI.Projects.DataGenerationJob", + "azure.ai.projects.models.DataGenerationJobInputs": "Azure.AI.Projects.DataGenerationJobInputs", + "azure.ai.projects.models.DataGenerationJobOptions": "Azure.AI.Projects.DataGenerationJobOptions", + "azure.ai.projects.models.DataGenerationJobOutput": "Azure.AI.Projects.DataGenerationJobOutput", + "azure.ai.projects.models.DataGenerationJobOutputOptions": "Azure.AI.Projects.DataGenerationJobOutputOptions", + "azure.ai.projects.models.DataGenerationJobResult": "Azure.AI.Projects.DataGenerationJobResult", + "azure.ai.projects.models.DataGenerationModelOptions": "Azure.AI.Projects.DataGenerationModelOptions", + "azure.ai.projects.models.DataGenerationTokenUsage": "Azure.AI.Projects.DataGenerationTokenUsage", "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", "azure.ai.projects.models.DeleteAgentVersionResponse": "Azure.AI.Projects.DeleteAgentVersionResponse", + "azure.ai.projects.models.DeleteMemoryResult": "Azure.AI.Projects.DeleteMemoryResponse", "azure.ai.projects.models.DeleteMemoryStoreResult": "Azure.AI.Projects.DeleteMemoryStoreResponse", - "azure.ai.projects.models.DeleteSkillResponse": "Azure.AI.Projects.DeleteSkillResponse", + "azure.ai.projects.models.DeleteSkillResult": "Azure.AI.Projects.DeleteSkillResponse", + "azure.ai.projects.models.DeleteSkillVersionResponse": "Azure.AI.Projects.DeleteSkillVersionResponse", "azure.ai.projects.models.Deployment": "Azure.AI.Projects.Deployment", + "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.EntraAuthorizationScheme": "Azure.AI.Projects.EntraAuthorizationScheme", "azure.ai.projects.models.EntraIDCredentials": "Azure.AI.Projects.EntraIDCredentials", @@ -117,19 +151,30 @@ "azure.ai.projects.models.ScheduleTask": "Azure.AI.Projects.ScheduleTask", "azure.ai.projects.models.EvaluationScheduleTask": "Azure.AI.Projects.EvaluationScheduleTask", "azure.ai.projects.models.EvaluationTaxonomy": "Azure.AI.Projects.EvaluationTaxonomy", + "azure.ai.projects.models.EvaluatorCredentialRequest": "Azure.AI.Projects.EvaluatorCredentialRequest", + "azure.ai.projects.models.EvaluatorGenerationArtifacts": "Azure.AI.Projects.EvaluatorGenerationArtifacts", + "azure.ai.projects.models.EvaluatorGenerationInputs": "Azure.AI.Projects.EvaluatorGenerationInputs", + "azure.ai.projects.models.EvaluatorGenerationJob": "Azure.AI.Projects.EvaluatorGenerationJob", + "azure.ai.projects.models.EvaluatorGenerationTokenUsage": "Azure.AI.Projects.EvaluatorGenerationTokenUsage", "azure.ai.projects.models.EvaluatorMetric": "Azure.AI.Projects.EvaluatorMetric", "azure.ai.projects.models.EvaluatorVersion": "Azure.AI.Projects.EvaluatorVersion", + "azure.ai.projects.models.ExternalAgentDefinition": "Azure.AI.Projects.ExternalAgentDefinition", "azure.ai.projects.models.FabricDataAgentToolParameters": "Azure.AI.Projects.FabricDataAgentToolParameters", + "azure.ai.projects.models.FabricIQPreviewTool": "Azure.AI.Projects.FabricIQPreviewTool", "azure.ai.projects.models.FieldMapping": "Azure.AI.Projects.FieldMapping", + "azure.ai.projects.models.FileDataGenerationJobOutput": "Azure.AI.Projects.FileDataGenerationJobOutput", + "azure.ai.projects.models.FileDataGenerationJobSource": "Azure.AI.Projects.FileDataGenerationJobSource", "azure.ai.projects.models.FileDatasetVersion": "Azure.AI.Projects.FileDatasetVersion", "azure.ai.projects.models.FileSearchTool": "OpenAI.FileSearchTool", "azure.ai.projects.models.VersionSelectionRule": "Azure.AI.Projects.VersionSelectionRule", "azure.ai.projects.models.FixedRatioVersionSelectionRule": "Azure.AI.Projects.FixedRatioVersionSelectionRule", "azure.ai.projects.models.FolderDatasetVersion": "Azure.AI.Projects.FolderDatasetVersion", + "azure.ai.projects.models.FoundryModelWarning": "Azure.AI.Projects.FoundryModelWarning", "azure.ai.projects.models.FunctionShellToolParam": "OpenAI.FunctionShellToolParam", "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.GitHubIssueRoutineTrigger": "Azure.AI.Projects.GitHubIssueRoutineTrigger", "azure.ai.projects.models.HeaderIsolationKeySource": "Azure.AI.Projects.HeaderIsolationKeySource", "azure.ai.projects.models.TelemetryEndpointAuth": "Azure.AI.Projects.TelemetryEndpointAuth", "azure.ai.projects.models.HeaderTelemetryEndpointAuth": "Azure.AI.Projects.HeaderTelemetryEndpointAuth", @@ -147,8 +192,15 @@ "azure.ai.projects.models.InsightScheduleTask": "Azure.AI.Projects.InsightScheduleTask", "azure.ai.projects.models.InsightsMetadata": "Azure.AI.Projects.InsightsMetadata", "azure.ai.projects.models.InsightSummary": "Azure.AI.Projects.InsightSummary", + "azure.ai.projects.models.RoutineDispatchPayload": "Azure.AI.Projects.RoutineDispatchPayload", + "azure.ai.projects.models.InvokeAgentInvocationsApiDispatchPayload": "Azure.AI.Projects.InvokeAgentInvocationsApiDispatchPayload", + "azure.ai.projects.models.RoutineAction": "Azure.AI.Projects.RoutineAction", + "azure.ai.projects.models.InvokeAgentInvocationsApiRoutineAction": "Azure.AI.Projects.InvokeAgentInvocationsApiRoutineAction", + "azure.ai.projects.models.InvokeAgentResponsesApiDispatchPayload": "Azure.AI.Projects.InvokeAgentResponsesApiDispatchPayload", + "azure.ai.projects.models.InvokeAgentResponsesApiRoutineAction": "Azure.AI.Projects.InvokeAgentResponsesApiRoutineAction", "azure.ai.projects.models.LocalShellToolParam": "OpenAI.LocalShellToolParam", "azure.ai.projects.models.LocalSkillParam": "OpenAI.LocalSkillParam", + "azure.ai.projects.models.LoraConfig": "Azure.AI.Projects.LoraConfig", "azure.ai.projects.models.ManagedAgentIdentityBlueprintReference": "Azure.AI.Projects.ManagedAgentIdentityBlueprintReference", "azure.ai.projects.models.ManagedAzureAISearchIndex": "Azure.AI.Projects.ManagedAzureAISearchIndex", "azure.ai.projects.models.MCPTool": "OpenAI.MCPTool", @@ -168,9 +220,14 @@ "azure.ai.projects.models.MemoryStoreUpdateCompletedResult": "Azure.AI.Projects.MemoryStoreUpdateCompletedResult", "azure.ai.projects.models.MemoryStoreUpdateResult": "Azure.AI.Projects.MemoryStoreUpdateResponse", "azure.ai.projects.models.MicrosoftFabricPreviewTool": "Azure.AI.Projects.MicrosoftFabricPreviewTool", + "azure.ai.projects.models.ModelCredentialRequest": "Azure.AI.Projects.ModelCredentialRequest", "azure.ai.projects.models.ModelDeployment": "Azure.AI.Projects.ModelDeployment", "azure.ai.projects.models.ModelDeploymentSku": "Azure.AI.Projects.Sku", + "azure.ai.projects.models.ModelPendingUploadRequest": "Azure.AI.Projects.ModelPendingUploadRequest", + "azure.ai.projects.models.ModelPendingUploadResponse": "Azure.AI.Projects.ModelPendingUploadResponse", "azure.ai.projects.models.ModelSamplingParams": "Azure.AI.Projects.ModelSamplingParams", + "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.NoAuthenticationCredentials": "Azure.AI.Projects.NoAuthenticationCredentials", "azure.ai.projects.models.OneTimeTrigger": "Azure.AI.Projects.OneTimeTrigger", @@ -183,13 +240,27 @@ "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.OptimizationCandidate": "Azure.AI.Projects.OptimizationCandidate", + "azure.ai.projects.models.OptimizationJob": "Azure.AI.Projects.OptimizationJob", + "azure.ai.projects.models.OptimizationJobInputs": "Azure.AI.Projects.OptimizationJobInputs", + "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.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", "azure.ai.projects.models.PromptBasedEvaluatorDefinition": "Azure.AI.Projects.PromptBasedEvaluatorDefinition", + "azure.ai.projects.models.PromptDataGenerationJobSource": "Azure.AI.Projects.PromptDataGenerationJobSource", + "azure.ai.projects.models.PromptEvaluatorGenerationJobSource": "Azure.AI.Projects.PromptEvaluatorGenerationJobSource", "azure.ai.projects.models.ProtocolVersionRecord": "Azure.AI.Projects.ProtocolVersionRecord", "azure.ai.projects.models.RaiConfig": "Azure.AI.Projects.RaiConfig", "azure.ai.projects.models.RankingOptions": "OpenAI.RankingOptions", @@ -198,22 +269,29 @@ "azure.ai.projects.models.RedTeam": "Azure.AI.Projects.RedTeam", "azure.ai.projects.models.ResponseUsageInputTokensDetails": "OpenAI.ResponseUsageInputTokensDetails", "azure.ai.projects.models.ResponseUsageOutputTokensDetails": "OpenAI.ResponseUsageOutputTokensDetails", + "azure.ai.projects.models.Routine": "Azure.AI.Projects.Routine", + "azure.ai.projects.models.RoutineRun": "Azure.AI.Projects.RoutineRun", + "azure.ai.projects.models.RubricBasedEvaluatorDefinition": "Azure.AI.Projects.RubricBasedEvaluatorDefinition", "azure.ai.projects.models.SASCredentials": "Azure.AI.Projects.SASCredentials", "azure.ai.projects.models.Schedule": "Azure.AI.Projects.Schedule", + "azure.ai.projects.models.ScheduleRoutineTrigger": "Azure.AI.Projects.ScheduleRoutineTrigger", "azure.ai.projects.models.ScheduleRun": "Azure.AI.Projects.ScheduleRun", "azure.ai.projects.models.SessionDirectoryEntry": "Azure.AI.Projects.SessionDirectoryEntry", - "azure.ai.projects.models.SessionDirectoryListResponse": "Azure.AI.Projects.SessionDirectoryListResponse", - "azure.ai.projects.models.SessionFileWriteResponse": "Azure.AI.Projects.SessionFileWriteResponse", + "azure.ai.projects.models.SessionFileWriteResult": "Azure.AI.Projects.SessionFileWriteResponse", "azure.ai.projects.models.SessionLogEvent": "Azure.AI.Projects.SessionLogEvent", "azure.ai.projects.models.SharepointGroundingToolParameters": "Azure.AI.Projects.SharepointGroundingToolParameters", "azure.ai.projects.models.SharepointPreviewTool": "Azure.AI.Projects.SharepointPreviewTool", - "azure.ai.projects.models.SkillObject": "Azure.AI.Projects.SkillObject", + "azure.ai.projects.models.SimpleQnADataGenerationJobOptions": "Azure.AI.Projects.SimpleQnADataGenerationJobOptions", + "azure.ai.projects.models.SkillDetails": "Azure.AI.Projects.Skill", + "azure.ai.projects.models.SkillInlineContent": "Azure.AI.Projects.SkillInlineContent", "azure.ai.projects.models.SkillReferenceParam": "OpenAI.SkillReferenceParam", + "azure.ai.projects.models.SkillVersion": "Azure.AI.Projects.SkillVersion", "azure.ai.projects.models.ToolChoiceParam": "OpenAI.ToolChoiceParam", "azure.ai.projects.models.SpecificApplyPatchParam": "OpenAI.SpecificApplyPatchParam", "azure.ai.projects.models.SpecificFunctionShellParam": "OpenAI.SpecificFunctionShellParam", "azure.ai.projects.models.StructuredInputDefinition": "Azure.AI.Projects.StructuredInputDefinition", "azure.ai.projects.models.StructuredOutputDefinition": "Azure.AI.Projects.StructuredOutputDefinition", + "azure.ai.projects.models.SystemDataV3": "Azure.AI.Projects.SystemDataV3", "azure.ai.projects.models.TaxonomyCategory": "Azure.AI.Projects.TaxonomyCategory", "azure.ai.projects.models.TaxonomySubCategory": "Azure.AI.Projects.TaxonomySubCategory", "azure.ai.projects.models.TelemetryConfig": "Azure.AI.Projects.TelemetryConfig", @@ -221,8 +299,12 @@ "azure.ai.projects.models.TextResponseFormatJsonObject": "OpenAI.TextResponseFormatConfigurationResponseFormatJsonObject", "azure.ai.projects.models.TextResponseFormatJsonSchema": "OpenAI.TextResponseFormatJsonSchema", "azure.ai.projects.models.TextResponseFormatText": "OpenAI.TextResponseFormatConfigurationResponseFormatText", + "azure.ai.projects.models.TimerRoutineTrigger": "Azure.AI.Projects.TimerRoutineTrigger", "azure.ai.projects.models.ToolboxObject": "Azure.AI.Projects.ToolboxObject", "azure.ai.projects.models.ToolboxPolicies": "Azure.AI.Projects.ToolboxPolicies", + "azure.ai.projects.models.ToolboxSearchPreviewTool": "Azure.AI.Projects.ToolboxSearchPreviewTool", + "azure.ai.projects.models.ToolboxSkill": "Azure.AI.Projects.ToolboxSkill", + "azure.ai.projects.models.ToolboxSkillReference": "Azure.AI.Projects.ToolboxSkillReference", "azure.ai.projects.models.ToolboxVersionObject": "Azure.AI.Projects.ToolboxVersionObject", "azure.ai.projects.models.ToolChoiceAllowed": "OpenAI.ToolChoiceAllowed", "azure.ai.projects.models.ToolChoiceCodeInterpreter": "OpenAI.ToolChoiceCodeInterpreter", @@ -234,8 +316,14 @@ "azure.ai.projects.models.ToolChoiceMCP": "OpenAI.ToolChoiceMCP", "azure.ai.projects.models.ToolChoiceWebSearchPreview": "OpenAI.ToolChoiceWebSearchPreview", "azure.ai.projects.models.ToolChoiceWebSearchPreview20250311": "OpenAI.ToolChoiceWebSearchPreview20250311", + "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.ToolUseFineTuningDataGenerationJobOptions": "Azure.AI.Projects.ToolUseFineTuningDataGenerationJobOptions", + "azure.ai.projects.models.TracesDataGenerationJobOptions": "Azure.AI.Projects.TracesDataGenerationJobOptions", + "azure.ai.projects.models.TracesDataGenerationJobSource": "Azure.AI.Projects.TracesDataGenerationJobSource", + "azure.ai.projects.models.TracesEvaluatorGenerationJobSource": "Azure.AI.Projects.TracesEvaluatorGenerationJobSource", + "azure.ai.projects.models.UpdateModelVersionRequest": "Azure.AI.Projects.UpdateModelVersionRequest", "azure.ai.projects.models.UpdateToolboxRequest": "Azure.AI.Projects.UpdateToolboxRequest", "azure.ai.projects.models.UserProfileMemoryItem": "Azure.AI.Projects.UserProfileMemoryItem", "azure.ai.projects.models.VersionIndicator": "Azure.AI.Projects.VersionIndicator", @@ -249,7 +337,6 @@ "azure.ai.projects.models.WeeklyRecurrenceSchedule": "Azure.AI.Projects.WeeklyRecurrenceSchedule", "azure.ai.projects.models.WorkflowAgentDefinition": "Azure.AI.Projects.WorkflowAgentDefinition", "azure.ai.projects.models.WorkIQPreviewTool": "Azure.AI.Projects.WorkIQPreviewTool", - "azure.ai.projects.models.WorkIQPreviewToolParameters": "Azure.AI.Projects.WorkIQPreviewToolParameters", "azure.ai.projects.models.AgentObjectType": "Azure.AI.Projects.AgentObjectType", "azure.ai.projects.models.AgentKind": "Azure.AI.Projects.AgentKind", "azure.ai.projects.models.ToolType": "OpenAI.ToolType", @@ -267,6 +354,7 @@ "azure.ai.projects.models.ContainerSkillType": "OpenAI.ContainerSkillType", "azure.ai.projects.models.SearchContextSize": "OpenAI.SearchContextSize", "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", "azure.ai.projects.models.TelemetryDataKind": "Azure.AI.Projects.TelemetryDataKind", "azure.ai.projects.models.TelemetryEndpointAuthType": "Azure.AI.Projects.TelemetryEndpointAuthType", @@ -283,6 +371,8 @@ "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.EvaluationLevel": "Azure.AI.Projects.EvaluationLevel", "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", @@ -290,6 +380,8 @@ "azure.ai.projects.models.EvaluatorDefinitionType": "Azure.AI.Projects.EvaluatorDefinitionType", "azure.ai.projects.models.EvaluatorMetricType": "Azure.AI.Projects.EvaluatorMetricType", "azure.ai.projects.models.EvaluatorMetricDirection": "Azure.AI.Projects.EvaluatorMetricDirection", + "azure.ai.projects.models.PendingUploadType": "Azure.AI.Projects.PendingUploadType", + "azure.ai.projects.models.EvaluatorGenerationJobSourceType": "Azure.AI.Projects.EvaluatorGenerationJobSourceType", "azure.ai.projects.models.OperationState": "Azure.Core.Foundations.OperationState", "azure.ai.projects.models.InsightType": "Azure.AI.Projects.InsightType", "azure.ai.projects.models.SampleType": "Azure.AI.Projects.SampleType", @@ -298,18 +390,33 @@ "azure.ai.projects.models.MemoryStoreKind": "Azure.AI.Projects.MemoryStoreKind", "azure.ai.projects.models.MemoryItemKind": "Azure.AI.Projects.MemoryItemKind", "azure.ai.projects.models.MemoryOperationKind": "Azure.AI.Projects.MemoryOperationKind", + "azure.ai.projects.models.FoundryModelWeightType": "Azure.AI.Projects.FoundryModelWeightType", + "azure.ai.projects.models.FoundryModelSourceType": "Azure.AI.Projects.FoundryModelSourceType", + "azure.ai.projects.models.FoundryModelArtifactProfileCategory": "Azure.AI.Projects.FoundryModelArtifactProfileCategory", + "azure.ai.projects.models.FoundryModelArtifactProfileSignal": "Azure.AI.Projects.FoundryModelArtifactProfileSignal", + "azure.ai.projects.models.FoundryModelWarningCode": "Azure.AI.Projects.FoundryModelWarningCode", "azure.ai.projects.models.AttackStrategy": "Azure.AI.Projects.AttackStrategy", + "azure.ai.projects.models.RoutineTriggerType": "Azure.AI.Projects.RoutineTriggerType", + "azure.ai.projects.models.GitHubIssueEvent": "Azure.AI.Projects.GitHubIssueEvent", + "azure.ai.projects.models.RoutineActionType": "Azure.AI.Projects.RoutineActionType", + "azure.ai.projects.models.RoutineRunPhase": "Azure.AI.Projects.RoutineRunPhase", + "azure.ai.projects.models.RoutineAttemptSource": "Azure.AI.Projects.RoutineAttemptSource", + "azure.ai.projects.models.RoutineDispatchPayloadType": "Azure.AI.Projects.RoutineDispatchPayloadType", "azure.ai.projects.models.ScheduleProvisioningStatus": "Azure.AI.Projects.ScheduleProvisioningStatus", "azure.ai.projects.models.TriggerType": "Azure.AI.Projects.TriggerType", "azure.ai.projects.models.RecurrenceType": "Azure.AI.Projects.RecurrenceType", "azure.ai.projects.models.DayOfWeek": "Azure.AI.Projects.DayOfWeek", "azure.ai.projects.models.ScheduleTaskType": "Azure.AI.Projects.ScheduleTaskType", + "azure.ai.projects.models.DataGenerationJobSourceType": "Azure.AI.Projects.DataGenerationJobSourceType", + "azure.ai.projects.models.DataGenerationJobType": "Azure.AI.Projects.DataGenerationJobType", + "azure.ai.projects.models.SimpleQnAFineTuningQuestionType": "Azure.AI.Projects.SimpleQnAFineTuningQuestionType", + "azure.ai.projects.models.DataGenerationJobScenario": "Azure.AI.Projects.DataGenerationJobScenario", + "azure.ai.projects.models.DataGenerationJobOutputType": "Azure.AI.Projects.DataGenerationJobOutputType", "azure.ai.projects.models.EvaluationRuleActionType": "Azure.AI.Projects.EvaluationRuleActionType", "azure.ai.projects.models.EvaluationRuleEventType": "Azure.AI.Projects.EvaluationRuleEventType", "azure.ai.projects.models.ConnectionType": "Azure.AI.Projects.ConnectionType", "azure.ai.projects.models.CredentialType": "Azure.AI.Projects.CredentialType", "azure.ai.projects.models.DatasetType": "Azure.AI.Projects.DatasetType", - "azure.ai.projects.models.PendingUploadType": "Azure.AI.Projects.PendingUploadType", "azure.ai.projects.models.DeploymentType": "Azure.AI.Projects.DeploymentType", "azure.ai.projects.models.IndexType": "Azure.AI.Projects.IndexType", "azure.ai.projects.models.MemoryStoreUpdateStatus": "Azure.AI.Projects.MemoryStoreUpdateStatus", diff --git a/sdk/ai/azure-ai-projects/assets.json b/sdk/ai/azure-ai-projects/assets.json index 87ee354cd9f9..61c5f9ecf55c 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_64257c2deb" + "Tag": "python/ai/azure-ai-projects_7fc443e02a" } diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/_patch.py b/sdk/ai/azure-ai-projects/azure/ai/projects/_patch.py index 950bfaf73334..8b68c0515aec 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/_patch.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/_patch.py @@ -24,6 +24,86 @@ logger = logging.getLogger(__name__) +# --------------------------------------------------------------------------- +# Shared helpers used by both the sync and async AIProjectClient.get_openai_client() +# implementations. Defined at module level so the async client can import and reuse +# them without duplicating the logic. +# --------------------------------------------------------------------------- + + +def _resolve_openai_base_url(config: Any, agent_name: Optional[str], kwargs: dict) -> str: + """Resolve the base URL for the (Async)OpenAI client. + + :param config: Generated client configuration carrying ``endpoint`` and ``allow_preview``. + :type config: Any + :param agent_name: Optional hosted-agent name. + :type agent_name: str or None + :param kwargs: Caller keyword arguments; ``base_url`` is popped when present. + :type kwargs: dict + :return: The base URL to use for the (Async)OpenAI client. + :rtype: str + :raises ValueError: If ``agent_name`` is provided but ``allow_preview=True`` was not set. + """ + if "base_url" in kwargs: + return kwargs.pop("base_url") + if agent_name is not None: + if config.allow_preview: + return config.endpoint.rstrip("/") + f"/agents/{agent_name}/endpoint/protocols/openai" + raise ValueError( + "Calling `get_openai_client` method with an `agent_name` requires you to set `allow_preview=True`" + "\nwhen constructing the AIProjectClient. Note that preview features are under development and " + "\nsubject to change. They should not be used in production environments." + ) + return config.endpoint.rstrip("/") + "/openai/v1" + + +def _resolve_openai_query_params(config: Any, agent_name: Optional[str], kwargs: dict) -> dict: + """Build the ``default_query`` dict for the (Async)OpenAI client. + + :param config: Generated client configuration carrying ``api_version``. + :type config: Any + :param agent_name: Optional hosted-agent name. + :type agent_name: str or None + :param kwargs: Caller keyword arguments; ``default_query`` is popped when present. + :type kwargs: dict + :return: Query parameters to forward to the (Async)OpenAI client. + :rtype: dict + """ + default_query = dict[str, str](kwargs.pop("default_query", None) or {}) + if agent_name is not None and "api-version" not in default_query: + default_query["api-version"] = config.api_version + return default_query + + +def _resolve_openai_default_headers(agent_name: Optional[str], kwargs: dict) -> dict: + """Build the ``default_headers`` dict for the (Async)OpenAI client. + + :param agent_name: Optional hosted-agent name. + :type agent_name: str or None + :param kwargs: Caller keyword arguments; ``default_headers`` is popped when present. + :type kwargs: dict + :return: Headers to forward to the (Async)OpenAI client. + :rtype: dict + """ + default_headers = dict[str, str](kwargs.pop("default_headers", None) or {}) + if agent_name is not None and not _has_header_case_insensitive(default_headers, _FOUNDRY_FEATURES_HEADER_NAME): + default_headers[_FOUNDRY_FEATURES_HEADER_NAME] = _BETA_OPERATION_FEATURE_HEADERS["agents"] + return default_headers + + +def _build_openai_user_agent(custom_user_agent: Optional[str], openai_default_user_agent: str) -> str: + """Build the SDK-prefixed User-Agent string for the (Async)OpenAI client. + + :param custom_user_agent: Caller-supplied user_agent kwarg captured at construction time. + :type custom_user_agent: str or None + :param openai_default_user_agent: The OpenAI client's own default user-agent. + :type openai_default_user_agent: str + :return: Combined User-Agent string. + :rtype: str + """ + return "-".join(ua for ua in [custom_user_agent, "AIProjectClient"] if ua) + " " + openai_default_user_agent + + class AIProjectClient(AIProjectClientGenerated): # pylint: disable=too-many-instance-attributes """AIProjectClient. @@ -101,6 +181,35 @@ def __init__( self.telemetry = TelemetryOperations(self) # type: ignore + def _get_openai_api_key(self, kwargs: dict): + """Resolve the API key for the OpenAI client. + + :param kwargs: Caller keyword arguments; ``api_key`` is popped when present. + :type kwargs: dict + :return: The API key string or a bearer-token-provider callable. + :rtype: str or Callable + """ + if "api_key" in kwargs: + return kwargs.pop("api_key") + return get_bearer_token_provider( + self._config.credential, # pylint: disable=protected-access + "https://ai.azure.com/.default", + ) + + def _get_openai_http_client(self, kwargs: dict): + """Resolve the HTTP transport client for the OpenAI client. + + :param kwargs: Caller keyword arguments; ``http_client`` is popped when present. + :type kwargs: dict + :return: An httpx.Client instance configured with logging transport, or ``None``. + :rtype: httpx.Client or None + """ + if "http_client" in kwargs: + return kwargs.pop("http_client") + if self._console_logging_enabled: + return httpx.Client(transport=_OpenAILoggingTransport()) + return None + @distributed_trace def get_openai_client(self, *, agent_name: Optional[str] = None, **kwargs: Any) -> OpenAI: """Get an authenticated OpenAI client from the `openai` package. @@ -131,51 +240,17 @@ def get_openai_client(self, *, agent_name: Optional[str] = None, **kwargs: Any) kwargs = kwargs.copy() if kwargs else {} - # Allow caller to override base_url - if "base_url" in kwargs: - base_url = kwargs.pop("base_url") - elif agent_name is not None: - if self._config.allow_preview: - base_url = ( - self._config.endpoint.rstrip("/") + f"/agents/{agent_name}/endpoint/protocols/openai" - ) # pylint: disable=protected-access - else: - raise ValueError( - "Calling `get_openai_client` method with an `agent_name` requires you to set `allow_preview=True`" - "\nwhen constructing the AIProjectClient. Note that preview features are under development and " - "\nsubject to change. They should not be used in production environments." - ) - else: - base_url = self._config.endpoint.rstrip("/") + "/openai/v1" # pylint: disable=protected-access - - default_query = dict[str, str](kwargs.pop("default_query", None) or {}) - if agent_name is not None and "api-version" not in default_query: - default_query["api-version"] = self._config.api_version # pylint: disable=protected-access + base_url = _resolve_openai_base_url(self._config, agent_name, kwargs) + default_query = _resolve_openai_query_params(self._config, agent_name, kwargs) logger.debug( # pylint: disable=specify-parameter-names-in-call "[get_openai_client] Creating OpenAI client using Entra ID authentication, base_url = `%s`", # pylint: disable=line-too-long base_url, ) - # Allow caller to override api_key, otherwise use token provider - if "api_key" in kwargs: - api_key = kwargs.pop("api_key") - else: - api_key = get_bearer_token_provider( - self._config.credential, # pylint: disable=protected-access - "https://ai.azure.com/.default", - ) - - if "http_client" in kwargs: - http_client = kwargs.pop("http_client") - elif self._console_logging_enabled: - http_client = httpx.Client(transport=_OpenAILoggingTransport()) - else: - http_client = None - - default_headers = dict[str, str](kwargs.pop("default_headers", None) or {}) - if agent_name is not None and not _has_header_case_insensitive(default_headers, _FOUNDRY_FEATURES_HEADER_NAME): - default_headers[_FOUNDRY_FEATURES_HEADER_NAME] = _BETA_OPERATION_FEATURE_HEADERS["agents"] + api_key = self._get_openai_api_key(kwargs) + http_client = self._get_openai_http_client(kwargs) + default_headers = _resolve_openai_default_headers(agent_name, kwargs) openai_custom_user_agent = default_headers.get("User-Agent", None) @@ -195,11 +270,7 @@ def _create_openai_client(**kwargs) -> OpenAI: if openai_custom_user_agent: final_user_agent = openai_custom_user_agent else: - final_user_agent = ( - "-".join(ua for ua in [self._custom_user_agent, "AIProjectClient"] if ua) - + " " - + openai_default_user_agent - ) + final_user_agent = _build_openai_user_agent(self._custom_user_agent, openai_default_user_agent) default_headers["User-Agent"] = final_user_agent diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/_patch.pyi b/sdk/ai/azure-ai-projects/azure/ai/projects/_patch.pyi index c40454fc9320..42009c1c227e 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/_patch.pyi +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/_patch.pyi @@ -108,6 +108,11 @@ class AIProjectClient(AIProjectClientGenerated): # To make mypy happy... otherwise imports of the below result in mypy "attr-defined" error class _AuthSecretsFilter(logging.Filter): ... +def _resolve_openai_base_url(config: Any, agent_name: Optional[str], kwargs: dict) -> str: ... +def _resolve_openai_query_params(config: Any, agent_name: Optional[str], kwargs: dict) -> dict: ... +def _resolve_openai_default_headers(agent_name: Optional[str], kwargs: dict) -> dict: ... +def _build_openai_user_agent(custom_user_agent: Optional[str], openai_default_user_agent: str) -> str: ... + __all__: List[str] = ["AIProjectClient"] def patch_sdk() -> None: ... 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 707b7d8fac75..74ca7f7b13ba 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 @@ -33,6 +33,17 @@ def prepare_multipart_form_data( body: Mapping[str, Any], multipart_fields: list[str], data_fields: list[str] ) -> 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. + 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): @@ -40,11 +51,4 @@ def prepare_multipart_form_data( elif multipart_entry: files.append((multipart_field, multipart_entry)) - # if files is empty, sdk core library can't handle multipart/form-data correctly, so - # we put data fields into files with filename as None to avoid that scenario. - 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)))) - return files diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/_version.py b/sdk/ai/azure-ai-projects/azure/ai/projects/_version.py index ccb75164d3bc..8abefc77cabd 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/_version.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/_version.py @@ -6,4 +6,4 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -VERSION = "2.1.0" +VERSION = "2.2.0" diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py index 34528053a55c..6eafa71053e6 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py @@ -16,8 +16,13 @@ from azure.core.tracing.decorator import distributed_trace from azure.core.credentials_async import AsyncTokenCredential from azure.identity.aio import get_bearer_token_provider -from .._patch import _AuthSecretsFilter -from ..models._patch import _BETA_OPERATION_FEATURE_HEADERS, _FOUNDRY_FEATURES_HEADER_NAME, _has_header_case_insensitive +from .._patch import ( + _AuthSecretsFilter, + _build_openai_user_agent, + _resolve_openai_base_url, + _resolve_openai_default_headers, + _resolve_openai_query_params, +) from ._client import AIProjectClient as AIProjectClientGenerated from .operations import TelemetryOperations @@ -101,6 +106,35 @@ def __init__( self.telemetry = TelemetryOperations(self) # type: ignore + def _get_openai_api_key(self, kwargs: dict): + """Resolve the API key for the AsyncOpenAI client. + + :param kwargs: Caller keyword arguments; ``api_key`` is popped when present. + :type kwargs: dict + :return: The API key string or a bearer-token-provider callable. + :rtype: str or Callable + """ + if "api_key" in kwargs: + return kwargs.pop("api_key") + return get_bearer_token_provider( + self._config.credential, # pylint: disable=protected-access + "https://ai.azure.com/.default", + ) + + def _get_openai_http_client(self, kwargs: dict): + """Resolve the HTTP transport client for the AsyncOpenAI client. + + :param kwargs: Caller keyword arguments; ``http_client`` is popped when present. + :type kwargs: dict + :return: An httpx.AsyncClient instance configured with logging transport, or ``None``. + :rtype: httpx.AsyncClient or None + """ + if "http_client" in kwargs: + return kwargs.pop("http_client") + if self._console_logging_enabled: + return httpx.AsyncClient(transport=_OpenAILoggingTransport()) + return None + @distributed_trace def get_openai_client(self, *, agent_name: Optional[str] = None, **kwargs: Any) -> AsyncOpenAI: """Get an authenticated AsyncOpenAI client from the `openai` package. @@ -131,51 +165,17 @@ def get_openai_client(self, *, agent_name: Optional[str] = None, **kwargs: Any) kwargs = kwargs.copy() if kwargs else {} - # Allow caller to override base_url - if "base_url" in kwargs: - base_url = kwargs.pop("base_url") - elif agent_name is not None: - if self._config.allow_preview: - base_url = ( - self._config.endpoint.rstrip("/") + f"/agents/{agent_name}/endpoint/protocols/openai" - ) # pylint: disable=protected-access - else: - raise ValueError( - "Calling `get_openai_client` method with an `agent_name` requires you to set `allow_preview=True`" - "\nwhen constructing the AIProjectClient. Note that preview features are under development and " - "\nsubject to change. They should not be used in production environments." - ) - else: - base_url = self._config.endpoint.rstrip("/") + "/openai/v1" # pylint: disable=protected-access - - default_query = dict[str, str](kwargs.pop("default_query", None) or {}) - if agent_name is not None and "api-version" not in default_query: - default_query["api-version"] = self._config.api_version # pylint: disable=protected-access + base_url = _resolve_openai_base_url(self._config, agent_name, kwargs) + default_query = _resolve_openai_query_params(self._config, agent_name, kwargs) logger.debug( # pylint: disable=specify-parameter-names-in-call "[get_openai_client] Creating OpenAI client using Entra ID authentication, base_url = `%s`", # pylint: disable=line-too-long base_url, ) - # Allow caller to override api_key, otherwise use token provider - if "api_key" in kwargs: - api_key = kwargs.pop("api_key") - else: - api_key = get_bearer_token_provider( - self._config.credential, # pylint: disable=protected-access - "https://ai.azure.com/.default", - ) - - if "http_client" in kwargs: - http_client = kwargs.pop("http_client") - elif self._console_logging_enabled: - http_client = httpx.AsyncClient(transport=_OpenAILoggingTransport()) - else: - http_client = None - - default_headers = dict[str, str](kwargs.pop("default_headers", None) or {}) - if agent_name is not None and not _has_header_case_insensitive(default_headers, _FOUNDRY_FEATURES_HEADER_NAME): - default_headers[_FOUNDRY_FEATURES_HEADER_NAME] = _BETA_OPERATION_FEATURE_HEADERS["agents"] + api_key = self._get_openai_api_key(kwargs) + http_client = self._get_openai_http_client(kwargs) + default_headers = _resolve_openai_default_headers(agent_name, kwargs) openai_custom_user_agent = default_headers.get("User-Agent", None) @@ -195,11 +195,7 @@ def _create_openai_client(**kwargs) -> AsyncOpenAI: if openai_custom_user_agent: final_user_agent = openai_custom_user_agent else: - final_user_agent = ( - "-".join(ua for ua in [self._custom_user_agent, "AIProjectClient"] if ua) - + " " - + openai_default_user_agent - ) + final_user_agent = _build_openai_user_agent(self._custom_user_agent, openai_default_user_agent) default_headers["User-Agent"] = final_user_agent 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 41e22e8fa2e8..a9cd70ff188a 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 @@ -33,8 +33,9 @@ from azure.core.utils import case_insensitive_dict from ... import models as _models -from ..._utils.model_base import SdkJSONEncoder, _deserialize, _failsafe_deserialize +from ..._utils.model_base import Model as _Model, SdkJSONEncoder, _deserialize, _failsafe_deserialize from ..._utils.serialization import Deserializer, Serializer +from ..._utils.utils import prepare_multipart_form_data from ...operations._operations import ( build_agents_create_version_from_manifest_request, build_agents_create_version_request, @@ -44,53 +45,104 @@ build_agents_get_version_request, build_agents_list_request, build_agents_list_versions_request, + build_beta_agents_cancel_optimization_job_request, + build_beta_agents_create_optimization_job_request, build_beta_agents_create_session_request, + build_beta_agents_create_version_from_code_request, + build_beta_agents_delete_optimization_job_request, build_beta_agents_delete_session_file_request, build_beta_agents_delete_session_request, + build_beta_agents_download_code_request, build_beta_agents_download_session_file_request, - build_beta_agents_get_session_files_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, + build_beta_datasets_create_generation_job_request, + build_beta_datasets_delete_generation_job_request, + build_beta_datasets_get_generation_job_request, + build_beta_datasets_list_generation_jobs_request, build_beta_evaluation_taxonomies_create_request, build_beta_evaluation_taxonomies_delete_request, build_beta_evaluation_taxonomies_get_request, build_beta_evaluation_taxonomies_list_request, build_beta_evaluation_taxonomies_update_request, + build_beta_evaluators_cancel_generation_job_request, + build_beta_evaluators_create_generation_job_request, build_beta_evaluators_create_version_request, + build_beta_evaluators_delete_generation_job_request, build_beta_evaluators_delete_version_request, + build_beta_evaluators_get_credentials_request, + build_beta_evaluators_get_generation_job_request, build_beta_evaluators_get_version_request, + build_beta_evaluators_list_generation_jobs_request, build_beta_evaluators_list_request, build_beta_evaluators_list_versions_request, + build_beta_evaluators_pending_upload_request, build_beta_evaluators_update_version_request, build_beta_insights_generate_request, build_beta_insights_get_request, build_beta_insights_list_request, + build_beta_memory_stores_create_memory_request, build_beta_memory_stores_create_request, + build_beta_memory_stores_delete_memory_request, build_beta_memory_stores_delete_request, build_beta_memory_stores_delete_scope_request, + build_beta_memory_stores_get_memory_request, build_beta_memory_stores_get_request, + build_beta_memory_stores_list_memories_request, build_beta_memory_stores_list_request, build_beta_memory_stores_search_memories_request, build_beta_memory_stores_update_memories_request, + build_beta_memory_stores_update_memory_request, build_beta_memory_stores_update_request, + build_beta_models_delete_request, + build_beta_models_get_credentials_request, + build_beta_models_get_request, + build_beta_models_list_request, + build_beta_models_list_versions_request, + build_beta_models_pending_create_version_request, + build_beta_models_pending_upload_request, + build_beta_models_update_request, build_beta_red_teams_create_request, build_beta_red_teams_get_request, build_beta_red_teams_list_request, + build_beta_routines_create_or_update_request, + build_beta_routines_delete_request, + build_beta_routines_disable_request, + build_beta_routines_dispatch_request, + build_beta_routines_enable_request, + build_beta_routines_get_request, + build_beta_routines_list_request, + build_beta_routines_list_runs_request, build_beta_schedules_create_or_update_request, build_beta_schedules_delete_request, build_beta_schedules_get_request, build_beta_schedules_get_run_request, build_beta_schedules_list_request, build_beta_schedules_list_runs_request, - build_beta_skills_create_from_package_request, + build_beta_skills_create_from_files_request, build_beta_skills_create_request, build_beta_skills_delete_request, + build_beta_skills_delete_version_request, build_beta_skills_download_request, + build_beta_skills_download_version_request, build_beta_skills_get_request, + build_beta_skills_get_version_request, build_beta_skills_list_request, + build_beta_skills_list_versions_request, build_beta_skills_update_request, build_beta_toolboxes_create_version_request, build_beta_toolboxes_delete_request, @@ -155,10 +207,13 @@ def __init__(self, *args, **kwargs) -> None: self.evaluators = BetaEvaluatorsOperations(self._client, self._config, self._serialize, self._deserialize) self.insights = BetaInsightsOperations(self._client, self._config, self._serialize, self._deserialize) self.memory_stores = BetaMemoryStoresOperations(self._client, self._config, self._serialize, self._deserialize) + self.models = BetaModelsOperations(self._client, self._config, self._serialize, self._deserialize) self.red_teams = BetaRedTeamsOperations(self._client, self._config, self._serialize, self._deserialize) + self.routines = BetaRoutinesOperations(self._client, self._config, self._serialize, self._deserialize) self.schedules = BetaSchedulesOperations(self._client, self._config, self._serialize, self._deserialize) self.toolboxes = BetaToolboxesOperations(self._client, self._config, self._serialize, self._deserialize) self.skills = BetaSkillsOperations(self._client, self._config, self._serialize, self._deserialize) + self.datasets = BetaDatasetsOperations(self._client, self._config, self._serialize, self._deserialize) class AgentsOperations: @@ -244,11 +299,19 @@ async def get(self, agent_name: str, **kwargs: Any) -> _models.AgentDetails: return deserialized # type: ignore @distributed_trace_async - async def delete(self, agent_name: str, **kwargs: Any) -> _models.DeleteAgentResponse: - """Deletes an agent. + 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 + 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. :param agent_name: The name of the agent to delete. Required. :type agent_name: str + :keyword force: For Hosted Agents, if true, force-deletes the agent even if its versions have + active sessions, cascading deletion to all associated sessions. Defaults to ``false``. This + value is not relevant for other Agent types. Default value is None. + :paramtype force: bool :return: DeleteAgentResponse. The DeleteAgentResponse is compatible with MutableMapping :rtype: ~azure.ai.projects.models.DeleteAgentResponse :raises ~azure.core.exceptions.HttpResponseError: @@ -268,6 +331,7 @@ async def delete(self, agent_name: str, **kwargs: Any) -> _models.DeleteAgentRes _request = build_agents_delete_request( agent_name=agent_name, + force=force, api_version=self._config.api_version, headers=_headers, params=_params, @@ -321,7 +385,7 @@ def list( """Returns the list of all agents. :keyword kind: Filter agents by kind. If not provided, all agents are returned. Known values - are: "prompt", "hosted", and "workflow". Default value is None. + are: "prompt", "hosted", "workflow", and "external". Default value is None. :paramtype kind: str or ~azure.ai.projects.models.AgentKind :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -885,14 +949,20 @@ async def get_version(self, agent_name: str, agent_version: str, **kwargs: Any) @distributed_trace_async async def delete_version( - self, agent_name: str, agent_version: str, **kwargs: Any + self, agent_name: str, agent_version: str, *, force: Optional[bool] = None, **kwargs: Any ) -> _models.DeleteAgentVersionResponse: - """Deletes a specific version of an agent. + """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. :param agent_name: The name of the agent to delete. Required. :type agent_name: str :param agent_version: The version of the agent to delete. Required. :type agent_version: str + :keyword force: For Hosted Agents, if true, force-deletes the version even if it has active + sessions, cascading deletion to all associated sessions. Defaults to ``false``. This value is + not relevant for other Agent types. Default value is None. + :paramtype force: bool :return: DeleteAgentVersionResponse. The DeleteAgentVersionResponse is compatible with MutableMapping :rtype: ~azure.ai.projects.models.DeleteAgentVersionResponse @@ -914,6 +984,7 @@ async def delete_version( _request = build_agents_delete_version_request( agent_name=agent_name, agent_version=agent_version, + force=force, api_version=self._config.api_version, headers=_headers, params=_params, @@ -2995,7 +3066,7 @@ async def create_or_update( return deserialized # type: ignore -class BetaAgentsOperations: +class BetaAgentsOperations: # pylint: disable=too-many-public-methods """ .. warning:: **DO NOT** instantiate this class directly. @@ -3018,7 +3089,7 @@ async def patch_agent_details( agent_name: str, *, content_type: str = "application/merge-patch+json", - agent_endpoint: Optional[_models.AgentEndpoint] = None, + agent_endpoint: Optional[_models.AgentEndpointConfig] = None, agent_card: Optional[_models.AgentCard] = None, **kwargs: Any ) -> _models.AgentDetails: @@ -3030,7 +3101,7 @@ async def patch_agent_details( Default value is "application/merge-patch+json". :paramtype content_type: str :keyword agent_endpoint: The endpoint configuration for the agent. Default value is None. - :paramtype agent_endpoint: ~azure.ai.projects.models.AgentEndpoint + :paramtype agent_endpoint: ~azure.ai.projects.models.AgentEndpointConfig :keyword agent_card: Optional agent card for the agent. Default value is None. :paramtype agent_card: ~azure.ai.projects.models.AgentCard :return: AgentDetails. The AgentDetails is compatible with MutableMapping @@ -3080,7 +3151,7 @@ async def patch_agent_details( agent_name: str, body: Union[JSON, IO[bytes]] = _Unset, *, - agent_endpoint: Optional[_models.AgentEndpoint] = None, + agent_endpoint: Optional[_models.AgentEndpointConfig] = None, agent_card: Optional[_models.AgentCard] = None, **kwargs: Any ) -> _models.AgentDetails: @@ -3091,7 +3162,7 @@ async def patch_agent_details( :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] :keyword agent_endpoint: The endpoint configuration for the agent. Default value is None. - :paramtype agent_endpoint: ~azure.ai.projects.models.AgentEndpoint + :paramtype agent_endpoint: ~azure.ai.projects.models.AgentEndpointConfig :keyword agent_card: Optional agent card for the agent. Default value is None. :paramtype agent_card: ~azure.ai.projects.models.AgentCard :return: AgentDetails. The AgentDetails is compatible with MutableMapping @@ -3166,13 +3237,232 @@ async def patch_agent_details( return deserialized # type: ignore + @overload + async def create_version_from_code( + self, + agent_name: str, + content: _models.CreateAgentVersionFromCodeContent, + *, + code_zip_sha256: str, + **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param content: Required. + :type content: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_version_from_code( + self, agent_name: str, content: JSON, *, code_zip_sha256: str, **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param content: Required. + :type content: JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def create_version_from_code( + self, + agent_name: str, + content: Union[_models.CreateAgentVersionFromCodeContent, JSON], + *, + code_zip_sha256: str, + **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param content: Is either a CreateAgentVersionFromCodeContent type or a JSON type. Required. + :type content: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent or JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.AgentVersionDetails] = kwargs.pop("cls", None) + + _body = content.as_dict() if isinstance(content, _Model) else content + _file_fields: list[str] = ["code"] + _data_fields: list[str] = ["metadata"] + _files = prepare_multipart_form_data(_body, _file_fields, _data_fields) + + _request = build_beta_agents_create_version_from_code_request( + agent_name=agent_name, + code_zip_sha256=code_zip_sha256, + api_version=self._config.api_version, + files=_files, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentVersionDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + 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. + Returns the previously-uploaded zip (``application/zip``). + + If ``agent_version`` is supplied, returns that version's code zip; otherwise + returns the latest version's code zip. + + The SHA-256 digest of the returned bytes matches the ``content_hash`` on the + resolved version's ``code_configuration``. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :keyword agent_version: The version of the agent whose code zip should be downloaded. + If omitted, the latest version's code zip is returned. Default value is None. + :paramtype agent_version: str + :return: AsyncIterator[bytes] + :rtype: AsyncIterator[bytes] + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None) + + _request = build_beta_agents_download_code_request( + agent_name=agent_name, + agent_version=agent_version, + 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) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", True) + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["x-ms-agent-version"] = self._deserialize("str", response.headers.get("x-ms-agent-version")) + response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + @overload async def create_session( self, agent_name: str, *, - isolation_key: str, version_indicator: _models.VersionIndicator, + user_isolation_key: Optional[str] = None, content_type: str = "application/json", agent_session_id: Optional[str] = None, **kwargs: Any @@ -3183,11 +3473,11 @@ async def create_session( :param agent_name: The name of the agent to create a session for. Required. :type agent_name: str - :keyword isolation_key: Isolation key used by the agent endpoint to enforce session ownership - for session-mutating operations. Required. - :paramtype isolation_key: str :keyword version_indicator: Determines which agent version backs the session. Required. :paramtype version_indicator: ~azure.ai.projects.models.VersionIndicator + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -3201,7 +3491,13 @@ async def create_session( @overload async def create_session( - self, agent_name: str, body: JSON, *, isolation_key: str, content_type: str = "application/json", **kwargs: Any + self, + agent_name: str, + body: JSON, + *, + user_isolation_key: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any ) -> _models.AgentSessionResource: """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 @@ -3211,9 +3507,9 @@ async def create_session( :type agent_name: str :param body: Required. :type body: JSON - :keyword isolation_key: Isolation key used by the agent endpoint to enforce session ownership - for session-mutating operations. Required. - :paramtype isolation_key: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -3228,7 +3524,7 @@ async def create_session( agent_name: str, body: IO[bytes], *, - isolation_key: str, + user_isolation_key: Optional[str] = None, content_type: str = "application/json", **kwargs: Any ) -> _models.AgentSessionResource: @@ -3240,9 +3536,9 @@ async def create_session( :type agent_name: str :param body: Required. :type body: IO[bytes] - :keyword isolation_key: Isolation key used by the agent endpoint to enforce session ownership - for session-mutating operations. Required. - :paramtype isolation_key: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -3257,8 +3553,8 @@ async def create_session( agent_name: str, body: Union[JSON, IO[bytes]] = _Unset, *, - isolation_key: str, version_indicator: _models.VersionIndicator = _Unset, + user_isolation_key: Optional[str] = None, agent_session_id: Optional[str] = None, **kwargs: Any ) -> _models.AgentSessionResource: @@ -3270,11 +3566,11 @@ async def create_session( :type agent_name: str :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] - :keyword isolation_key: Isolation key used by the agent endpoint to enforce session ownership - for session-mutating operations. Required. - :paramtype isolation_key: str :keyword version_indicator: Determines which agent version backs the session. Required. :paramtype version_indicator: ~azure.ai.projects.models.VersionIndicator + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str :keyword agent_session_id: Optional caller-provided session ID. If specified, it must be unique within the agent endpoint. Auto-generated if omitted. Default value is None. :paramtype agent_session_id: str @@ -3310,7 +3606,7 @@ async def create_session( _request = build_beta_agents_create_session_request( agent_name=agent_name, - isolation_key=isolation_key, + user_isolation_key=user_isolation_key, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -3354,13 +3650,18 @@ async def create_session( return deserialized # type: ignore @distributed_trace_async - async def get_session(self, agent_name: str, session_id: str, **kwargs: Any) -> _models.AgentSessionResource: + 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. :param agent_name: The name of the agent. Required. :type agent_name: str :param session_id: The session identifier. Required. :type session_id: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str :return: AgentSessionResource. The AgentSessionResource is compatible with MutableMapping :rtype: ~azure.ai.projects.models.AgentSessionResource :raises ~azure.core.exceptions.HttpResponseError: @@ -3381,6 +3682,7 @@ async def get_session(self, agent_name: str, session_id: str, **kwargs: Any) -> _request = build_beta_agents_get_session_request( agent_name=agent_name, session_id=session_id, + user_isolation_key=user_isolation_key, api_version=self._config.api_version, headers=_headers, params=_params, @@ -3422,7 +3724,9 @@ async def get_session(self, agent_name: str, session_id: str, **kwargs: Any) -> return deserialized # type: ignore @distributed_trace_async - async def delete_session(self, agent_name: str, session_id: str, *, isolation_key: str, **kwargs: Any) -> None: + 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 exist. @@ -3430,9 +3734,9 @@ async def delete_session(self, agent_name: str, session_id: str, *, isolation_ke :type agent_name: str :param session_id: The session identifier. Required. :type session_id: str - :keyword isolation_key: Isolation key used by the agent endpoint to enforce session ownership - for session-mutating operations. Required. - :paramtype isolation_key: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str :return: None :rtype: None :raises ~azure.core.exceptions.HttpResponseError: @@ -3453,7 +3757,62 @@ async def delete_session(self, agent_name: str, session_id: str, *, isolation_ke _request = build_beta_agents_delete_session_request( agent_name=agent_name, session_id=session_id, - isolation_key=isolation_key, + user_isolation_key=user_isolation_key, + 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) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + 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, None, {}) # type: ignore + + @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. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param session_id: The session identifier. Required. + :type session_id: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_agents_stop_session_request( + agent_name=agent_name, + session_id=session_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -3486,6 +3845,7 @@ def list_sessions( self, agent_name: str, *, + user_isolation_key: Optional[str] = None, limit: Optional[int] = None, order: Optional[Union[str, _models.PageOrder]] = None, before: Optional[str] = None, @@ -3495,6 +3855,9 @@ def list_sessions( :param agent_name: The name of the agent. Required. :type agent_name: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. Default value is None. @@ -3530,6 +3893,7 @@ def prepare_request(_continuation_token=None): _request = build_beta_agents_list_sessions_request( agent_name=agent_name, + user_isolation_key=user_isolation_key, limit=limit, order=order, after=_continuation_token, @@ -3678,8 +4042,15 @@ async def get_session_log_stream( @distributed_trace_async async def _upload_session_file( - self, agent_name: str, agent_session_id: str, content: bytes, *, path: str, **kwargs: Any - ) -> _models.SessionFileWriteResponse: + self, + agent_name: str, + agent_session_id: str, + content: bytes, + *, + path: str, + 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. @@ -3692,9 +4063,11 @@ async def _upload_session_file( :keyword path: The destination file path within the sandbox, relative to the session home directory. Required. :paramtype path: str - :return: SessionFileWriteResponse. The SessionFileWriteResponse is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.SessionFileWriteResponse + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :return: SessionFileWriteResult. The SessionFileWriteResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SessionFileWriteResult :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -3709,7 +4082,7 @@ async def _upload_session_file( _params = kwargs.pop("params", {}) or {} content_type: str = kwargs.pop("content_type", _headers.pop("Content-Type", "application/octet-stream")) - cls: ClsType[_models.SessionFileWriteResponse] = kwargs.pop("cls", None) + cls: ClsType[_models.SessionFileWriteResult] = kwargs.pop("cls", None) _content = content @@ -3717,6 +4090,7 @@ async def _upload_session_file( agent_name=agent_name, agent_session_id=agent_session_id, path=path, + user_isolation_key=user_isolation_key, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -3752,7 +4126,7 @@ async def _upload_session_file( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.SessionFileWriteResponse, response.json()) + deserialized = _deserialize(_models.SessionFileWriteResult, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -3761,7 +4135,13 @@ async def _upload_session_file( @distributed_trace_async async def download_session_file( - self, agent_name: str, agent_session_id: str, *, path: str, **kwargs: Any + self, + agent_name: str, + agent_session_id: str, + *, + path: str, + user_isolation_key: Optional[str] = None, + **kwargs: Any ) -> AsyncIterator[bytes]: """Download a file from the session sandbox as a binary stream. @@ -3772,6 +4152,9 @@ async def download_session_file( :keyword path: The file path to download from the sandbox, relative to the session home directory. Required. :paramtype path: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str :return: AsyncIterator[bytes] :rtype: AsyncIterator[bytes] :raises ~azure.core.exceptions.HttpResponseError: @@ -3793,6 +4176,7 @@ async def download_session_file( agent_name=agent_name, agent_session_id=agent_session_id, path=path, + user_isolation_key=user_isolation_key, api_version=self._config.api_version, headers=_headers, params=_params, @@ -3830,25 +4214,58 @@ async def download_session_file( return deserialized # type: ignore - @distributed_trace_async - async def get_session_files( - self, agent_name: str, agent_session_id: str, *, path: str, **kwargs: Any - ) -> _models.SessionDirectoryListResponse: + @distributed_trace + def list_session_files( + self, + agent_name: str, + agent_session_id: str, + *, + path: Optional[str] = None, + user_isolation_key: Optional[str] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + 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). + children of the specified directory (non-recursive). If path is not provided, lists the session + home directory. :param agent_name: The name of the agent. Required. :type agent_name: str :param agent_session_id: The session ID. Required. :type agent_session_id: str - :keyword path: The directory path to list, relative to the session home directory. Required. + :keyword path: The directory path to list, relative to the session home directory. Defaults to + the home directory if not provided. Default value is None. :paramtype path: str - :return: SessionDirectoryListResponse. The SessionDirectoryListResponse is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.SessionDirectoryListResponse - :raises ~azure.core.exceptions.HttpResponseError: - """ - error_map: MutableMapping = { + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of SessionDirectoryEntry + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.SessionDirectoryEntry] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.SessionDirectoryEntry]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, @@ -3856,58 +4273,68 @@ async def get_session_files( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[_models.SessionDirectoryListResponse] = kwargs.pop("cls", None) + def prepare_request(_continuation_token=None): - _request = build_beta_agents_get_session_files_request( - agent_name=agent_name, - agent_session_id=agent_session_id, - path=path, - 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_session_files_request( + agent_name=agent_name, + agent_session_id=agent_session_id, + path=path, + user_isolation_key=user_isolation_key, + 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.SessionDirectoryEntry], + deserialized.get("entries", []), + ) + 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.SessionDirectoryListResponse, 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 delete_session_file( - self, agent_name: str, agent_session_id: str, *, path: str, recursive: Optional[bool] = None, **kwargs: Any + self, + agent_name: str, + agent_session_id: str, + *, + path: str, + recursive: Optional[bool] = None, + 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. @@ -3922,6 +4349,9 @@ async def delete_session_file( :keyword recursive: Whether to recursively delete directory contents. Defaults to false. Default value is None. :paramtype recursive: bool + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str :return: None :rtype: None :raises ~azure.core.exceptions.HttpResponseError: @@ -3944,6 +4374,7 @@ async def delete_session_file( agent_session_id=agent_session_id, path=path, recursive=recursive, + user_isolation_key=user_isolation_key, api_version=self._config.api_version, headers=_headers, params=_params, @@ -3971,32 +4402,103 @@ async def delete_session_file( if cls: return cls(pipeline_response, None, {}) # type: ignore + @overload + async def create_optimization_job( + self, + inputs: _models.OptimizationJobInputs, + *, + operation_id: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.OptimizationJob: + """Creates an agent optimization job. -class BetaEvaluationTaxonomiesOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. + Create an optimization job. Returns 201 with the queued job. Honours ``Operation-Id`` for + idempotent retry. - Instead, you should access the following operations through - :class:`~azure.ai.projects.aio.AIProjectClient`'s - :attr:`evaluation_taxonomies` attribute. - """ + :param inputs: The optimization job inputs. Required. + :type inputs: ~azure.ai.projects.models.OptimizationJobInputs + :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 + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + @overload + async def create_optimization_job( + self, inputs: 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 + :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 + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_optimization_job( + self, + inputs: IO[bytes], + *, + 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: 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 + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ @distributed_trace_async - async def get(self, name: str, **kwargs: Any) -> _models.EvaluationTaxonomy: - """Get an evaluation run by name. + async def create_optimization_job( + self, + inputs: Union[_models.OptimizationJobInputs, JSON, IO[bytes]], + *, + operation_id: Optional[str] = None, + **kwargs: Any + ) -> _models.OptimizationJob: + """Creates an agent optimization job. - :param name: The name of the resource. Required. - :type name: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + 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] + :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 + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4007,14 +4509,24 @@ async def get(self, name: str, **kwargs: Any) -> _models.EvaluationTaxonomy: } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.OptimizationJob] = kwargs.pop("cls", None) - _request = build_beta_evaluation_taxonomies_get_request( - name=name, + content_type = content_type or "application/json" + _content = None + if isinstance(inputs, (IOBase, bytes)): + _content = inputs + else: + _content = json.dumps(inputs, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_agents_create_optimization_job_request( + operation_id=operation_id, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -4031,44 +4543,45 @@ async def get(self, name: str, **kwargs: Any) -> _models.EvaluationTaxonomy: response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [201]: 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) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) + response_headers["Location"] = self._deserialize("str", response.headers.get("Location")) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) + deserialized = _deserialize(_models.OptimizationJob, response.json()) if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, deserialized, response_headers) # type: ignore return deserialized # type: ignore - @distributed_trace - def list( - self, *, input_name: Optional[str] = None, input_type: Optional[str] = None, **kwargs: Any - ) -> AsyncItemPaged["_models.EvaluationTaxonomy"]: - """List evaluation taxonomies. + @distributed_trace_async + async def get_optimization_job(self, job_id: str, **kwargs: Any) -> _models.OptimizationJob: + """Get info about an agent optimization job. - :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. - :paramtype input_type: str - :return: An iterator like instance of EvaluationTaxonomy - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.EvaluationTaxonomy] + Get an optimization job by id. Returns 202 while in progress, 200 when terminal. + + :param job_id: The ID of the job. Required. + :type job_id: str + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[List[_models.EvaluationTaxonomy]] = kwargs.pop("cls", None) - error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -4077,60 +4590,138 @@ def list( } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(next_link=None): - if not next_link: + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - _request = build_beta_evaluation_taxonomies_list_request( - input_name=input_name, - input_type=input_type, - 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) + cls: ClsType[_models.OptimizationJob] = kwargs.pop("cls", None) - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", - urllib.parse.urljoin(next_link, _parsed_next_link.path), - params=_next_request_params, - headers=_headers, - ) - 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_get_optimization_job_request( + job_id=job_id, + 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.EvaluationTaxonomy], - deserialized.get("value", []), - ) + response = pipeline_response.http_response + + if response.status_code not in [200, 202]: + 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, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Retry-After"] = self._deserialize("int", response.headers.get("Retry-After")) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.OptimizationJob, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_optimization_jobs( + self, + *, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + status: Optional[Union[str, _models.JobStatus]] = None, + agent_name: Optional[str] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.OptimizationJob"]: + """Returns a list of agent optimization jobs. + + List optimization jobs. Supports cursor pagination and optional status / agent_name filters. + + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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 + :keyword status: Filter to jobs in this lifecycle state. Known values are: "queued", + "in_progress", "succeeded", "failed", and "cancelled". Default value is None. + :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] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.OptimizationJob]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_beta_agents_list_optimization_jobs_request( + limit=limit, + order=order, + after=_continuation_token, + before=before, + status=status, + agent_name=agent_name, + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.OptimizationJob], + deserialized.get("data", []), + ) if cls: list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + return deserialized.get("last_id") or None, AsyncList(list_of_elem) - async def get_next(next_link=None): - _request = prepare_request(next_link) + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) _stream = False pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access @@ -4140,20 +4731,26 @@ async def get_next(next_link=None): if response.status_code not in [200]: map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) return pipeline_response return AsyncItemPaged(get_next, extract_data) @distributed_trace_async - async def delete(self, name: str, **kwargs: Any) -> None: - """Delete an evaluation taxonomy by name. + async def cancel_optimization_job(self, job_id: str, **kwargs: Any) -> _models.OptimizationJob: + """Cancels an agent optimization job. - :param name: The name of the resource. Required. - :type name: str - :return: None - :rtype: None + Request cancellation. Idempotent on terminal states. + + :param job_id: The ID of the job to cancel. Required. + :type job_id: str + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4167,10 +4764,10 @@ async def delete(self, name: str, **kwargs: Any) -> None: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[None] = kwargs.pop("cls", None) + cls: ClsType[_models.OptimizationJob] = kwargs.pop("cls", None) - _request = build_beta_evaluation_taxonomies_delete_request( - name=name, + _request = build_beta_agents_cancel_optimization_job_request( + job_id=job_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -4180,87 +4777,50 @@ async def delete(self, name: str, **kwargs: Any) -> None: } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _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 ) response = pipeline_response.http_response - if response.status_code not in [204]: + 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) - raise HttpResponseError(response=response) - - if cls: - return cls(pipeline_response, None, {}) # type: ignore - - @overload - async def create( - self, name: str, body: _models.EvaluationTaxonomy, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Create an evaluation taxonomy. - - :param name: The name of the evaluation taxonomy. Required. - :type name: str - :param body: The evaluation taxonomy. Required. - :type body: ~azure.ai.projects.models.EvaluationTaxonomy - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - async def create( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Create an evaluation taxonomy. + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) - :param name: The name of the evaluation taxonomy. Required. - :type name: str - :param body: The evaluation taxonomy. Required. - :type body: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy - :raises ~azure.core.exceptions.HttpResponseError: - """ + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.OptimizationJob, response.json()) - @overload - async def create( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Create an evaluation taxonomy. + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore - :param name: The name of the evaluation taxonomy. Required. - :type name: str - :param body: The evaluation taxonomy. Required. - :type body: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy - :raises ~azure.core.exceptions.HttpResponseError: - """ + return deserialized # type: ignore @distributed_trace_async - async def create( - self, name: str, body: Union[_models.EvaluationTaxonomy, JSON, IO[bytes]], **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Create an evaluation taxonomy. + async def delete_optimization_job(self, job_id: str, *, force: Optional[bool] = None, **kwargs: Any) -> None: + """Deletes an agent optimization job. - :param name: The name of the evaluation taxonomy. Required. - :type name: str - :param body: The evaluation taxonomy. Is one of the following types: EvaluationTaxonomy, JSON, - IO[bytes] Required. - :type body: ~azure.ai.projects.models.EvaluationTaxonomy or JSON or IO[bytes] - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + 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: """ error_map: MutableMapping = { @@ -4271,24 +4831,15 @@ async def create( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[None] = kwargs.pop("cls", None) - _request = build_beta_evaluation_taxonomies_create_request( - name=name, - content_type=content_type, + _request = build_beta_agents_delete_optimization_job_request( + job_id=job_id, + force=force, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -4297,100 +4848,64 @@ async def create( } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = False pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - if response.status_code not in [200, 201]: - if _stream: - try: - await response.read() # Load the body in memory and close the socket - except (StreamConsumedError, StreamClosedError): - pass + if response.status_code not in [204]: map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore - - return deserialized # type: ignore + return cls(pipeline_response, None, {}) # type: ignore - @overload - async def update( - self, name: str, body: _models.EvaluationTaxonomy, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Update an evaluation taxonomy. + @distributed_trace_async + async 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: + """Returns a list of candidates for an optimization job. - :param name: The name of the evaluation taxonomy. Required. - :type name: str - :param body: The evaluation taxonomy. Required. - :type body: ~azure.ai.projects.models.EvaluationTaxonomy - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy - :raises ~azure.core.exceptions.HttpResponseError: - """ + List candidates produced by a job. - @overload - async def update( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Update an evaluation taxonomy. - - :param name: The name of the evaluation taxonomy. Required. - :type name: str - :param body: The evaluation taxonomy. Required. - :type body: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - async def update( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Update an evaluation taxonomy. - - :param name: The name of the evaluation taxonomy. Required. - :type name: str - :param body: The evaluation taxonomy. Required. - :type body: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @distributed_trace_async - async def update( - self, name: str, body: Union[_models.EvaluationTaxonomy, JSON, IO[bytes]], **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Update an evaluation taxonomy. - - :param name: The name of the evaluation taxonomy. Required. - :type name: str - :param body: The evaluation taxonomy. Is one of the following types: EvaluationTaxonomy, JSON, - IO[bytes] Required. - :type body: ~azure.ai.projects.models.EvaluationTaxonomy or JSON or IO[bytes] - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :param job_id: The optimization job id. Required. + :type job_id: str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4401,24 +4916,18 @@ async def update( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[_models.AgentsPagedResultOptimizationCandidate] = kwargs.pop("cls", None) - _request = build_beta_evaluation_taxonomies_update_request( - name=name, - content_type=content_type, + _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, - content=_content, headers=_headers, params=_params, ) @@ -4442,65 +4951,38 @@ async def update( except (StreamConsumedError, StreamClosedError): pass map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) + deserialized = _deserialize(_models.AgentsPagedResultOptimizationCandidate, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + @distributed_trace_async + async def get_optimization_candidate( + self, job_id: str, candidate_id: str, **kwargs: Any + ) -> _models.CandidateMetadata: + """Get a candidate by id. -class BetaEvaluatorsOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.aio.AIProjectClient`'s - :attr:`evaluators` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - - @distributed_trace - def list_versions( - self, - name: str, - *, - type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, - limit: Optional[int] = None, - **kwargs: Any - ) -> AsyncItemPaged["_models.EvaluatorVersion"]: - """List all versions of the given evaluator. + Get a single candidate's metadata, manifest, and promotion info. - :param name: The name of the resource. Required. - :type name: str - :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 - value is None. - :paramtype type: str or str or str or str - :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the default is 20. Default value is None. - :paramtype limit: int - :return: An iterator like instance of EvaluatorVersion - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.EvaluatorVersion] + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id. Required. + :type candidate_id: str + :return: CandidateMetadata. The CandidateMetadata is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CandidateMetadata :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[List[_models.EvaluatorVersion]] = kwargs.pop("cls", None) - error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -4509,102 +4991,71 @@ def list_versions( } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(next_link=None): - if not next_link: - - _request = build_beta_evaluators_list_versions_request( - name=name, - type=type, - limit=limit, - 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) + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", - urllib.parse.urljoin(next_link, _parsed_next_link.path), - params=_next_request_params, - headers=_headers, - ) - 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) + cls: ClsType[_models.CandidateMetadata] = kwargs.pop("cls", None) - return _request + _request = build_beta_agents_get_optimization_candidate_request( + job_id=job_id, + candidate_id=candidate_id, + 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) - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.EvaluatorVersion], - deserialized.get("value", []), - ) - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + _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 get_next(next_link=None): - _request = prepare_request(next_link) + response = pipeline_response.http_response - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs + 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, ) - response = pipeline_response.http_response + raise HttpResponseError(response=response, model=error) - if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.CandidateMetadata, response.json()) - return pipeline_response + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore - return AsyncItemPaged(get_next, extract_data) + return deserialized # type: ignore - @distributed_trace - def list( - self, - *, - type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, - limit: Optional[int] = None, - **kwargs: Any - ) -> AsyncItemPaged["_models.EvaluatorVersion"]: - """List the latest version of each evaluator. + @distributed_trace_async + async def get_optimization_candidate_config( + self, job_id: str, candidate_id: str, **kwargs: Any + ) -> _models.CandidateDeployConfig: + """Get candidate deploy config. - :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 - value is None. - :paramtype type: str or str or str or str - :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the default is 20. Default value is None. - :paramtype limit: int - :return: An iterator like instance of EvaluatorVersion - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.EvaluatorVersion] + Get the candidate's deploy config JSON. Used to compose ``agents.create_version(...)`` from a + candidate. + + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id. Required. + :type candidate_id: str + :return: CandidateDeployConfig. The CandidateDeployConfig is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CandidateDeployConfig :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[List[_models.EvaluatorVersion]] = kwargs.pop("cls", None) - error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -4613,86 +5064,68 @@ def list( } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(next_link=None): - if not next_link: + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - _request = build_beta_evaluators_list_request( - type=type, - limit=limit, - 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) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", - urllib.parse.urljoin(next_link, _parsed_next_link.path), - params=_next_request_params, - headers=_headers, - ) - 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) + cls: ClsType[_models.CandidateDeployConfig] = kwargs.pop("cls", None) - return _request + _request = build_beta_agents_get_optimization_candidate_config_request( + job_id=job_id, + candidate_id=candidate_id, + 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) - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.EvaluatorVersion], - deserialized.get("value", []), - ) - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + _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 get_next(next_link=None): - _request = prepare_request(next_link) + response = pipeline_response.http_response - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs + 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, ) - response = pipeline_response.http_response + raise HttpResponseError(response=response, model=error) - if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.CandidateDeployConfig, response.json()) - return pipeline_response + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore - return AsyncItemPaged(get_next, extract_data) + return deserialized # type: ignore @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. + async def get_optimization_candidate_results( + self, job_id: str, candidate_id: str, **kwargs: Any + ) -> _models.CandidateResults: + """Get candidate evaluation results. - :param name: The name of the resource. Required. - :type name: str - :param version: The specific version id of the EvaluatorVersion to retrieve. Required. - :type version: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + Get full per-task evaluation results for a candidate. + + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id. Required. + :type candidate_id: str + :return: CandidateResults. The CandidateResults is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CandidateResults :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4706,11 +5139,11 @@ async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.E _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) + cls: ClsType[_models.CandidateResults] = kwargs.pop("cls", None) - _request = build_beta_evaluators_get_version_request( - name=name, - version=version, + _request = build_beta_agents_get_optimization_candidate_results_request( + job_id=job_id, + candidate_id=candidate_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -4735,12 +5168,16 @@ async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.E except (StreamConsumedError, StreamClosedError): pass map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluatorVersion, response.json()) + deserialized = _deserialize(_models.CandidateResults, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -4748,16 +5185,21 @@ async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.E return deserialized # type: ignore @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. + async def get_candidate_file( + self, job_id: str, candidate_id: str, *, path: str, **kwargs: Any + ) -> AsyncIterator[bytes]: + """Get a candidate file. - :param name: The name of the resource. Required. - :type name: str - :param version: The version of the EvaluatorVersion to delete. Required. - :type version: str - :return: None - :rtype: None + Stream a specific file from the candidate's blob directory. + + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id. Required. + :type candidate_id: str + :keyword path: Relative path of the file to download (e.g. 'files/examples.jsonl'). Required. + :paramtype path: str + :return: AsyncIterator[bytes] + :rtype: AsyncIterator[bytes] :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4771,11 +5213,12 @@ async def delete_version(self, name: str, version: str, **kwargs: Any) -> None: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[None] = kwargs.pop("cls", None) + cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None) - _request = build_beta_evaluators_delete_version_request( - name=name, - version=version, + _request = build_beta_agents_get_candidate_file_request( + job_id=job_id, + candidate_id=candidate_id, + path=path, api_version=self._config.api_version, headers=_headers, params=_params, @@ -4785,92 +5228,143 @@ async def delete_version(self, name: str, version: str, **kwargs: Any) -> None: } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", True) pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - if response.status_code not in [204]: + 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) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() if cls: - return cls(pipeline_response, None, {}) # type: ignore + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore @overload - async def create_version( + async def promote_candidate( self, - name: str, - evaluator_version: _models.EvaluatorVersion, + job_id: str, + candidate_id: str, + candidate_request: _models.PromoteCandidateRequest, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluatorVersion: - """Create a new EvaluatorVersion with auto incremented version id. + ) -> _models.PromoteCandidateResponse: + """Promote a candidate. - :param name: The name of the resource. Required. - :type name: str - :param evaluator_version: Required. - :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion + Promotes a candidate, recording the deployment timestamp and target agent version. + + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id to promote. Required. + :type candidate_id: str + :param candidate_request: Promotion details. Required. + :type candidate_request: ~azure.ai.projects.models.PromoteCandidateRequest :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :return: PromoteCandidateResponse. The PromoteCandidateResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.PromoteCandidateResponse :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + async def promote_candidate( + self, + job_id: str, + candidate_id: str, + candidate_request: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.PromoteCandidateResponse: + """Promote a candidate. - :param name: The name of the resource. Required. - :type name: str - :param evaluator_version: Required. - :type evaluator_version: JSON + Promotes a candidate, recording the deployment timestamp and target agent version. + + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id to promote. Required. + :type candidate_id: str + :param candidate_request: Promotion details. Required. + :type candidate_request: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :return: PromoteCandidateResponse. The PromoteCandidateResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.PromoteCandidateResponse :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + async def promote_candidate( + self, + job_id: str, + candidate_id: str, + candidate_request: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.PromoteCandidateResponse: + """Promote a candidate. - :param name: The name of the resource. Required. - :type name: str - :param evaluator_version: Required. - :type evaluator_version: IO[bytes] + Promotes a candidate, recording the deployment timestamp and target agent version. + + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id to promote. Required. + :type candidate_id: str + :param candidate_request: Promotion details. Required. + :type candidate_request: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :return: PromoteCandidateResponse. The PromoteCandidateResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.PromoteCandidateResponse :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace_async - 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. - - :param name: The name of the resource. Required. - :type name: str - :param evaluator_version: Is one of the following types: EvaluatorVersion, JSON, IO[bytes] - Required. - :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion or JSON or IO[bytes] - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + async def promote_candidate( + self, + job_id: str, + candidate_id: str, + candidate_request: Union[_models.PromoteCandidateRequest, JSON, IO[bytes]], + **kwargs: Any + ) -> _models.PromoteCandidateResponse: + """Promote a candidate. + + Promotes a candidate, recording the deployment timestamp and target agent version. + + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id to promote. Required. + :type candidate_id: str + :param candidate_request: Promotion details. Is one of the following types: + PromoteCandidateRequest, JSON, IO[bytes] Required. + :type candidate_request: ~azure.ai.projects.models.PromoteCandidateRequest or JSON or IO[bytes] + :return: PromoteCandidateResponse. The PromoteCandidateResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.PromoteCandidateResponse :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4885,17 +5379,18 @@ async def create_version( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) + cls: ClsType[_models.PromoteCandidateResponse] = kwargs.pop("cls", None) content_type = content_type or "application/json" _content = None - if isinstance(evaluator_version, (IOBase, bytes)): - _content = evaluator_version + if isinstance(candidate_request, (IOBase, bytes)): + _content = candidate_request else: - _content = json.dumps(evaluator_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(candidate_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_evaluators_create_version_request( - name=name, + _request = build_beta_agents_promote_candidate_request( + job_id=job_id, + candidate_id=candidate_id, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -4915,116 +5410,55 @@ async def create_version( response = pipeline_response.http_response - if response.status_code not in [201]: + 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) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluatorVersion, response.json()) + deserialized = _deserialize(_models.PromoteCandidateResponse, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @overload - async def update_version( - self, - name: str, - version: str, - evaluator_version: _models.EvaluatorVersion, - *, - content_type: str = "application/json", - **kwargs: Any - ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. - :param name: The name of the resource. Required. - :type name: str - :param version: The version of the EvaluatorVersion to update. Required. - :type version: str - :param evaluator_version: Evaluator resource. Required. - :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion - :raises ~azure.core.exceptions.HttpResponseError: - """ +class BetaEvaluationTaxonomiesOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. - @overload - 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. + Instead, you should access the following operations through + :class:`~azure.ai.projects.aio.AIProjectClient`'s + :attr:`evaluation_taxonomies` attribute. + """ - :param name: The name of the resource. Required. - :type name: str - :param version: The version of the EvaluatorVersion to update. Required. - :type version: str - :param evaluator_version: Evaluator resource. Required. - :type evaluator_version: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - async def update_version( - self, - name: str, - version: str, - evaluator_version: IO[bytes], - *, - content_type: str = "application/json", - **kwargs: Any - ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. - - :param name: The name of the resource. Required. - :type name: str - :param version: The version of the EvaluatorVersion to update. Required. - :type version: str - :param evaluator_version: Evaluator resource. Required. - :type evaluator_version: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion - :raises ~azure.core.exceptions.HttpResponseError: - """ + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") @distributed_trace_async - async def update_version( - self, - name: str, - version: str, - evaluator_version: Union[_models.EvaluatorVersion, JSON, IO[bytes]], - **kwargs: Any - ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + async def get(self, name: str, **kwargs: Any) -> _models.EvaluationTaxonomy: + """Get an evaluation run by name. :param name: The name of the resource. Required. :type name: str - :param version: The version of the EvaluatorVersion to update. Required. - :type version: str - :param evaluator_version: Evaluator resource. Is one of the following types: EvaluatorVersion, - JSON, IO[bytes] Required. - :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion or JSON or IO[bytes] - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -5035,25 +5469,14 @@ async def update_version( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _content = None - if isinstance(evaluator_version, (IOBase, bytes)): - _content = evaluator_version - else: - _content = json.dumps(evaluator_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) - _request = build_beta_evaluators_update_version_request( + _request = build_beta_evaluation_taxonomies_get_request( name=name, - version=version, - content_type=content_type, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -5082,91 +5505,117 @@ async def update_version( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluatorVersion, response.json()) + deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + @distributed_trace + def list( + self, *, input_name: Optional[str] = None, input_type: Optional[str] = None, **kwargs: Any + ) -> AsyncItemPaged["_models.EvaluationTaxonomy"]: + """List evaluation taxonomies. -class BetaInsightsOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. + :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. + :paramtype input_type: str + :return: An iterator like instance of EvaluationTaxonomy + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.EvaluationTaxonomy] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - Instead, you should access the following operations through - :class:`~azure.ai.projects.aio.AIProjectClient`'s - :attr:`insights` attribute. - """ + cls: ClsType[List[_models.EvaluationTaxonomy]] = kwargs.pop("cls", None) - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) - @overload - async def generate( - self, insight: _models.Insight, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.Insight: - """Generate Insights. + def prepare_request(next_link=None): + if not next_link: - :param insight: Complete evaluation configuration including data source, evaluators, and result - settings. Required. - :type insight: ~azure.ai.projects.models.Insight - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: Insight. The Insight is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Insight - :raises ~azure.core.exceptions.HttpResponseError: - """ + _request = build_beta_evaluation_taxonomies_list_request( + input_name=input_name, + input_type=input_type, + 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) - @overload - async def generate( - self, insight: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.Insight: - """Generate Insights. + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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) - :param insight: Complete evaluation configuration including data source, evaluators, and result - settings. Required. - :type insight: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: Insight. The Insight is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Insight - :raises ~azure.core.exceptions.HttpResponseError: - """ + return _request - @overload - async def generate( - self, insight: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.Insight: - """Generate Insights. + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.EvaluationTaxonomy], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) - :param insight: Complete evaluation configuration including data source, evaluators, and result - settings. Required. - :type insight: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: Insight. The Insight is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Insight - :raises ~azure.core.exceptions.HttpResponseError: - """ + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) @distributed_trace_async - async def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwargs: Any) -> _models.Insight: - """Generate Insights. + async def delete(self, name: str, **kwargs: Any) -> None: + """Delete an evaluation taxonomy by name. - :param insight: Complete evaluation configuration including data source, evaluators, and result - settings. Is one of the following types: Insight, JSON, IO[bytes] Required. - :type insight: ~azure.ai.projects.models.Insight or JSON or IO[bytes] - :return: Insight. The Insight is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Insight + :param name: The name of the resource. Required. + :type name: str + :return: None + :rtype: None :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -5177,23 +5626,14 @@ async def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwa } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.Insight] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _content = None - if isinstance(insight, (IOBase, bytes)): - _content = insight - else: - _content = json.dumps(insight, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[None] = kwargs.pop("cls", None) - _request = build_beta_insights_generate_request( - content_type=content_type, + _request = build_beta_evaluation_taxonomies_delete_request( + name=name, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -5202,50 +5642,87 @@ async def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwa } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = False pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - if response.status_code not in [201]: - if _stream: - try: - await response.read() # Load the body in memory and close the socket - except (StreamConsumedError, StreamClosedError): - pass + if response.status_code not in [204]: 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 _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.Insight, response.json()) + raise HttpResponseError(response=response) if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, None, {}) # type: ignore - return deserialized # type: ignore + @overload + async def create( + self, name: str, taxonomy: _models.EvaluationTaxonomy, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Create an evaluation taxonomy. + + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: ~azure.ai.projects.models.EvaluationTaxonomy + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create( + self, name: str, taxonomy: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Create an evaluation taxonomy. + + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create( + self, name: str, taxonomy: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Create an evaluation taxonomy. + + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :raises ~azure.core.exceptions.HttpResponseError: + """ @distributed_trace_async - async def get( - self, insight_id: str, *, include_coordinates: Optional[bool] = None, **kwargs: Any - ) -> _models.Insight: - """Get a specific insight by Id. + async def create( + self, name: str, taxonomy: Union[_models.EvaluationTaxonomy, JSON, IO[bytes]], **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Create an evaluation taxonomy. - :param insight_id: The unique identifier for the insights report. Required. - :type insight_id: str - :keyword include_coordinates: Whether to include coordinates for visualization in the response. - Defaults to false. Default value is None. - :paramtype include_coordinates: bool - :return: Insight. The Insight is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Insight + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Is one of the following types: EvaluationTaxonomy, + JSON, IO[bytes] Required. + :type taxonomy: ~azure.ai.projects.models.EvaluationTaxonomy or JSON or IO[bytes] + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -5256,15 +5733,24 @@ async def get( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.Insight] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) - _request = build_beta_insights_get_request( - insight_id=insight_id, - include_coordinates=include_coordinates, + content_type = content_type or "application/json" + _content = None + if isinstance(taxonomy, (IOBase, bytes)): + _content = taxonomy + else: + _content = json.dumps(taxonomy, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_evaluation_taxonomies_create_request( + name=name, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -5281,63 +5767,94 @@ async def get( response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [200, 201]: 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, - ) - raise HttpResponseError(response=response, model=error) + raise HttpResponseError(response=response) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.Insight, response.json()) + deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @distributed_trace - def list( - self, - *, - type: Optional[Union[str, _models.InsightType]] = None, - eval_id: Optional[str] = None, - run_id: Optional[str] = None, - agent_name: Optional[str] = None, - include_coordinates: Optional[bool] = None, - **kwargs: Any - ) -> AsyncItemPaged["_models.Insight"]: - """List all insights in reverse chronological order (newest first). + @overload + async def update( + self, name: str, taxonomy: _models.EvaluationTaxonomy, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Update an evaluation taxonomy. - :keyword type: Filter by the type of analysis. Known values are: "EvaluationRunClusterInsight", - "AgentClusterInsight", and "EvaluationComparison". Default value is None. - :paramtype type: str or ~azure.ai.projects.models.InsightType - :keyword eval_id: Filter by the evaluation ID. Default value is None. - :paramtype eval_id: str - :keyword run_id: Filter by the evaluation run ID. Default value is None. - :paramtype run_id: str - :keyword agent_name: Filter by the agent name. Default value is None. - :paramtype agent_name: str - :keyword include_coordinates: Whether to include coordinates for visualization in the response. - Defaults to false. Default value is None. - :paramtype include_coordinates: bool - :return: An iterator like instance of Insight - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.Insight] + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: ~azure.ai.projects.models.EvaluationTaxonomy + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.Insight]] = kwargs.pop("cls", None) + @overload + async def update( + self, name: str, taxonomy: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Update an evaluation taxonomy. + + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update( + self, name: str, taxonomy: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Update an evaluation taxonomy. + + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def update( + self, name: str, taxonomy: Union[_models.EvaluationTaxonomy, JSON, IO[bytes]], **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """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, + JSON, IO[bytes] Required. + :type taxonomy: ~azure.ai.projects.models.EvaluationTaxonomy or JSON or IO[bytes] + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :raises ~azure.core.exceptions.HttpResponseError: + """ error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -5346,91 +5863,68 @@ def list( } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(next_link=None): - if not next_link: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} - _request = build_beta_insights_list_request( - type=type, - eval_id=eval_id, - run_id=run_id, - agent_name=agent_name, - include_coordinates=include_coordinates, - 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) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", - urllib.parse.urljoin(next_link, _parsed_next_link.path), - params=_next_request_params, - headers=_headers, - ) - 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) + content_type = content_type or "application/json" + _content = None + if isinstance(taxonomy, (IOBase, bytes)): + _content = taxonomy + else: + _content = json.dumps(taxonomy, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - return _request + _request = build_beta_evaluation_taxonomies_update_request( + name=name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.Insight], - deserialized.get("value", []), - ) - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + _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 get_next(next_link=None): - _request = prepare_request(next_link) + response = pipeline_response.http_response - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response + 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) + raise HttpResponseError(response=response) - 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 _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) - return pipeline_response + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore - return AsyncItemPaged(get_next, extract_data) + return deserialized # type: ignore -class BetaMemoryStoresOperations: +class BetaEvaluatorsOperations: """ .. warning:: **DO NOT** instantiate this class directly. Instead, you should access the following operations through :class:`~azure.ai.projects.aio.AIProjectClient`'s - :attr:`memory_stores` attribute. + :attr:`evaluators` attribute. """ def __init__(self, *args, **kwargs) -> None: @@ -5440,96 +5934,139 @@ def __init__(self, *args, **kwargs) -> None: self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - @overload - async def create( + @distributed_trace + def list_versions( self, - *, name: str, - definition: _models.MemoryStoreDefinition, - content_type: str = "application/json", - description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, + *, + type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, + limit: Optional[int] = None, **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Create a memory store. + ) -> AsyncItemPaged["_models.EvaluatorVersion"]: + """List all versions of the given evaluator. - :keyword name: The name of the memory store. Required. - :paramtype name: str - :keyword definition: The memory store definition. Required. - :paramtype definition: ~azure.ai.projects.models.MemoryStoreDefinition - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :keyword description: A human-readable description of the memory store. Default value is None. - :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default + :param name: The name of the resource. Required. + :type name: str + :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 value is None. - :paramtype metadata: dict[str, str] - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :paramtype type: str or str or str or str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. Default value is None. + :paramtype limit: int + :return: An iterator like instance of EvaluatorVersion + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.EvaluatorVersion] :raises ~azure.core.exceptions.HttpResponseError: """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - @overload - async def create( - self, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Create a memory store. + cls: ClsType[List[_models.EvaluatorVersion]] = kwargs.pop("cls", None) - :param body: Required. - :type body: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails - :raises ~azure.core.exceptions.HttpResponseError: - """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) - @overload - async def create( - self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Create a memory store. + def prepare_request(next_link=None): + if not next_link: - :param body: Required. - :type body: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails - :raises ~azure.core.exceptions.HttpResponseError: - """ + _request = build_beta_evaluators_list_versions_request( + name=name, + type=type, + limit=limit, + 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) - @distributed_trace_async - async def create( + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.EvaluatorVersion], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace + def list( self, - body: Union[JSON, IO[bytes]] = _Unset, *, - name: str = _Unset, - definition: _models.MemoryStoreDefinition = _Unset, - description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, + type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, + limit: Optional[int] = None, **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Create a memory store. + ) -> AsyncItemPaged["_models.EvaluatorVersion"]: + """List the latest version of each evaluator. - :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. - :paramtype name: str - :keyword definition: The memory store definition. Required. - :paramtype definition: ~azure.ai.projects.models.MemoryStoreDefinition - :keyword description: A human-readable description of the memory store. Default value is None. - :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default + :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 value is None. - :paramtype metadata: dict[str, str] - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :paramtype type: str or str or str or str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. Default value is None. + :paramtype limit: int + :return: An iterator like instance of EvaluatorVersion + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.EvaluatorVersion] :raises ~azure.core.exceptions.HttpResponseError: """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.EvaluatorVersion]] = kwargs.pop("cls", None) + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -5538,30 +6075,105 @@ async def create( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} + def prepare_request(next_link=None): + if not next_link: - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) + _request = build_beta_evaluators_list_request( + type=type, + limit=limit, + 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) - if body is _Unset: - if name is _Unset: - raise TypeError("missing required argument: name") - if definition is _Unset: - raise TypeError("missing required argument: definition") - body = {"definition": definition, "description": description, "metadata": metadata, "name": name} - body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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_memory_stores_create_request( - content_type=content_type, + return _request + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.EvaluatorVersion], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @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. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the EvaluatorVersion to retrieve. Required. + :type version: str + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) + + _request = build_beta_evaluators_get_version_request( + name=name, + version=version, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -5585,108 +6197,142 @@ async def create( except (StreamConsumedError, StreamClosedError): pass 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) + raise HttpResponseError(response=response) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) + deserialized = _deserialize(_models.EvaluatorVersion, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + @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. + + :param name: The name of the resource. Required. + :type name: str + :param version: The version of the EvaluatorVersion to delete. Required. + :type version: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_evaluators_delete_version_request( + name=name, + version=version, + 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) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if cls: + return cls(pipeline_response, None, {}) # type: ignore + @overload - async def update( + async def create_version( self, name: str, + evaluator_version: _models.EvaluatorVersion, *, content_type: str = "application/json", - description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Update a memory store. + ) -> _models.EvaluatorVersion: + """Create a new EvaluatorVersion with auto incremented version id. - :param name: The name of the memory store to update. Required. + :param name: The name of the resource. Required. :type name: str + :param evaluator_version: Required. + :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :keyword description: A human-readable description of the memory store. Default value is None. - :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default - value is None. - :paramtype metadata: dict[str, str] - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ @overload - async def update( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Update a memory store. + 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. - :param name: The name of the memory store to update. Required. + :param name: The name of the resource. Required. :type name: str - :param body: Required. - :type body: JSON + :param evaluator_version: Required. + :type evaluator_version: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ @overload - async def update( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Update a memory store. + 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. - :param name: The name of the memory store to update. Required. + :param name: The name of the resource. Required. :type name: str - :param body: Required. - :type body: IO[bytes] + :param evaluator_version: Required. + :type evaluator_version: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace_async - async def update( - self, - name: str, - body: Union[JSON, IO[bytes]] = _Unset, - *, - description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Update a memory store. + 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. - :param name: The name of the memory store to update. Required. + :param name: The name of the resource. Required. :type name: str - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword description: A human-readable description of the memory store. Default value is None. - :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default - value is None. - :paramtype metadata: dict[str, str] - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :param evaluator_version: Is one of the following types: EvaluatorVersion, JSON, IO[bytes] + Required. + :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion or JSON or IO[bytes] + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -5701,19 +6347,16 @@ async def update( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) + cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) - if body is _Unset: - body = {"description": description, "metadata": metadata} - body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None - if isinstance(body, (IOBase, bytes)): - _content = body + if isinstance(evaluator_version, (IOBase, bytes)): + _content = evaluator_version else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(evaluator_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_memory_stores_update_request( + _request = build_beta_evaluators_create_version_request( name=name, content_type=content_type, api_version=self._config.api_version, @@ -5734,192 +6377,116 @@ async def update( response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [201]: 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, - ) - raise HttpResponseError(response=response, model=error) + raise HttpResponseError(response=response) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) + deserialized = _deserialize(_models.EvaluatorVersion, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @distributed_trace_async - async def get(self, name: str, **kwargs: Any) -> _models.MemoryStoreDetails: - """Retrieve a memory store. + @overload + async def update_version( + self, + name: str, + version: str, + evaluator_version: _models.EvaluatorVersion, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.EvaluatorVersion: + """Update an existing EvaluatorVersion with the given version id. - :param name: The name of the memory store to retrieve. Required. + :param name: The name of the resource. Required. :type name: str - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :param version: The version of the EvaluatorVersion to update. Required. + :type version: str + :param evaluator_version: Evaluator resource. Required. + :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) - - _request = build_beta_memory_stores_get_request( - name=name, - 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) - - _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 - ) - - response = pipeline_response.http_response - - 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, - ) - raise HttpResponseError(response=response, model=error) - - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + @overload + 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. - return deserialized # type: ignore + :param name: The name of the resource. Required. + :type name: str + :param version: The version of the EvaluatorVersion to update. Required. + :type version: str + :param evaluator_version: Evaluator resource. Required. + :type evaluator_version: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ - @distributed_trace - def list( + @overload + async def update_version( self, + name: str, + version: str, + evaluator_version: IO[bytes], *, - limit: Optional[int] = None, - order: Optional[Union[str, _models.PageOrder]] = None, - before: Optional[str] = None, + content_type: str = "application/json", **kwargs: Any - ) -> AsyncItemPaged["_models.MemoryStoreDetails"]: - """List all memory stores. + ) -> _models.EvaluatorVersion: + """Update an existing EvaluatorVersion with the given version id. - :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the - default is 20. Default value is None. - :paramtype limit: int - :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for - 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 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: An iterator like instance of MemoryStoreDetails - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.MemoryStoreDetails] + :param name: The name of the resource. Required. + :type name: str + :param version: The version of the EvaluatorVersion to update. Required. + :type version: str + :param evaluator_version: Evaluator resource. Required. + :type evaluator_version: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[List[_models.MemoryStoreDetails]] = kwargs.pop("cls", None) - - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(_continuation_token=None): - - _request = build_beta_memory_stores_list_request( - 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 - - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.MemoryStoreDetails], - 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) - - async def get_next(_continuation_token=None): - _request = prepare_request(_continuation_token) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - 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) - - return pipeline_response - - return AsyncItemPaged(get_next, extract_data) @distributed_trace_async - async def delete(self, name: str, **kwargs: Any) -> _models.DeleteMemoryStoreResult: - """Delete a memory store. + async def update_version( + self, + name: str, + version: str, + evaluator_version: Union[_models.EvaluatorVersion, JSON, IO[bytes]], + **kwargs: Any + ) -> _models.EvaluatorVersion: + """Update an existing EvaluatorVersion with the given version id. - :param name: The name of the memory store to delete. Required. + :param name: The name of the resource. Required. :type name: str - :return: DeleteMemoryStoreResult. The DeleteMemoryStoreResult is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.DeleteMemoryStoreResult + :param version: The version of the EvaluatorVersion to update. Required. + :type version: str + :param evaluator_version: Evaluator resource. Is one of the following types: EvaluatorVersion, + JSON, IO[bytes] Required. + :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion or JSON or IO[bytes] + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -5930,14 +6497,25 @@ async def delete(self, name: str, **kwargs: Any) -> _models.DeleteMemoryStoreRes } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.DeleteMemoryStoreResult] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) - _request = build_beta_memory_stores_delete_request( + content_type = content_type or "application/json" + _content = None + if isinstance(evaluator_version, (IOBase, bytes)): + _content = evaluator_version + else: + _content = json.dumps(evaluator_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_evaluators_update_version_request( name=name, + version=version, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -5961,16 +6539,12 @@ async def delete(self, name: str, **kwargs: Any) -> _models.DeleteMemoryStoreRes except (StreamConsumedError, StreamClosedError): pass 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) + raise HttpResponseError(response=response) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.DeleteMemoryStoreResult, response.json()) + deserialized = _deserialize(_models.EvaluatorVersion, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -5978,56 +6552,103 @@ async def delete(self, name: str, **kwargs: Any) -> _models.DeleteMemoryStoreRes return deserialized # type: ignore @overload - async def _search_memories( + async def pending_upload( self, name: str, + version: str, + pending_upload_request: _models.PendingUploadRequest, *, - scope: str, content_type: str = "application/json", - items: Optional[List[dict[str, Any]]] = None, - previous_search_id: Optional[str] = None, - options: Optional[_models.MemorySearchOptions] = None, **kwargs: Any - ) -> _models.MemoryStoreSearchResult: ... + ) -> _models.PendingUploadResponse: + """Start a new or get an existing pending upload of an evaluator for a specific version. + + :param name: Required. + :type name: str + :param version: The specific version id of the EvaluatorVersion to operate on. Required. + :type version: str + :param pending_upload_request: The pending upload request parameters. Required. + :type pending_upload_request: ~azure.ai.projects.models.PendingUploadRequest + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + @overload - async def _search_memories( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreSearchResult: ... + async def pending_upload( + self, + name: str, + version: str, + pending_upload_request: JSON, + *, + 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. + + :param name: Required. + :type name: str + :param version: The specific version id of the EvaluatorVersion to operate on. Required. + :type version: str + :param pending_upload_request: The pending upload request parameters. Required. + :type pending_upload_request: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + @overload - async def _search_memories( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreSearchResult: ... + async def pending_upload( + self, + name: str, + version: str, + pending_upload_request: IO[bytes], + *, + 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. + + :param name: Required. + :type name: str + :param version: The specific version id of the EvaluatorVersion to operate on. Required. + :type version: str + :param pending_upload_request: The pending upload request parameters. Required. + :type pending_upload_request: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ @distributed_trace_async - async def _search_memories( + async def pending_upload( self, name: str, - body: Union[JSON, IO[bytes]] = _Unset, - *, - scope: str = _Unset, - items: Optional[List[dict[str, Any]]] = None, - previous_search_id: Optional[str] = None, - options: Optional[_models.MemorySearchOptions] = None, + version: str, + pending_upload_request: Union[_models.PendingUploadRequest, JSON, IO[bytes]], **kwargs: Any - ) -> _models.MemoryStoreSearchResult: - """Search for relevant memories from a memory store based on conversation context. + ) -> _models.PendingUploadResponse: + """Start a new or get an existing pending upload of an evaluator for a specific version. - :param name: The name of the memory store to search. Required. + :param name: Required. :type name: str - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. - Required. - :paramtype scope: str - :keyword items: Items for which to search for relevant memories. Default value is None. - :paramtype items: list[dict[str, any]] - :keyword previous_search_id: The unique ID of the previous search request, enabling incremental - memory search from where the last operation left off. Default value is None. - :paramtype previous_search_id: str - :keyword options: Memory search options. Default value is None. - :paramtype options: ~azure.ai.projects.models.MemorySearchOptions - :return: MemoryStoreSearchResult. The MemoryStoreSearchResult is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreSearchResult + :param version: The specific version id of the EvaluatorVersion to operate on. Required. + :type version: str + :param pending_upload_request: The pending upload request parameters. Is one of the following + types: PendingUploadRequest, JSON, IO[bytes] Required. + :type pending_upload_request: ~azure.ai.projects.models.PendingUploadRequest or JSON or + IO[bytes] + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -6042,27 +6663,18 @@ async def _search_memories( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.MemoryStoreSearchResult] = kwargs.pop("cls", None) + cls: ClsType[_models.PendingUploadResponse] = kwargs.pop("cls", None) - if body is _Unset: - if scope is _Unset: - raise TypeError("missing required argument: scope") - body = { - "items": items, - "options": options, - "previous_search_id": previous_search_id, - "scope": scope, - } - body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None - if isinstance(body, (IOBase, bytes)): - _content = body + if isinstance(pending_upload_request, (IOBase, bytes)): + _content = pending_upload_request else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(pending_upload_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_memory_stores_search_memories_request( + _request = build_beta_evaluators_pending_upload_request( name=name, + version=version, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -6098,24 +6710,113 @@ async def _search_memories( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.MemoryStoreSearchResult, response.json()) + deserialized = _deserialize(_models.PendingUploadResponse, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - async def _update_memories_initial( + @overload + async def get_credentials( self, name: str, - body: Union[JSON, IO[bytes]] = _Unset, + version: str, + credential_request: _models.EvaluatorCredentialRequest, *, - scope: str = _Unset, - items: Optional[List[dict[str, Any]]] = None, - previous_update_id: Optional[str] = None, - update_delay: Optional[int] = None, + content_type: str = "application/json", **kwargs: Any - ) -> AsyncIterator[bytes]: + ) -> _models.DatasetCredential: + """Get the SAS credential to access the storage account associated with an Evaluator version. + + :param name: Required. + :type name: str + :param version: The specific version id of the EvaluatorVersion to operate on. Required. + :type version: str + :param credential_request: The credential request parameters. Required. + :type credential_request: ~azure.ai.projects.models.EvaluatorCredentialRequest + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def get_credentials( + self, + name: str, + version: str, + credential_request: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.DatasetCredential: + """Get the SAS credential to access the storage account associated with an Evaluator version. + + :param name: Required. + :type name: str + :param version: The specific version id of the EvaluatorVersion to operate on. Required. + :type version: str + :param credential_request: The credential request parameters. Required. + :type credential_request: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def get_credentials( + self, + name: str, + version: str, + credential_request: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.DatasetCredential: + """Get the SAS credential to access the storage account associated with an Evaluator version. + + :param name: Required. + :type name: str + :param version: The specific version id of the EvaluatorVersion to operate on. Required. + :type version: str + :param credential_request: The credential request parameters. Required. + :type credential_request: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def get_credentials( + self, + name: str, + version: str, + 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. + + :param name: Required. + :type name: str + :param version: The specific version id of the EvaluatorVersion to operate on. Required. + :type version: str + :param credential_request: The credential request parameters. Is one of the following types: + EvaluatorCredentialRequest, JSON, IO[bytes] Required. + :type credential_request: ~azure.ai.projects.models.EvaluatorCredentialRequest or JSON or + IO[bytes] + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential + :raises ~azure.core.exceptions.HttpResponseError: + """ error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -6128,27 +6829,18 @@ async def _update_memories_initial( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None) + cls: ClsType[_models.DatasetCredential] = kwargs.pop("cls", None) - if body is _Unset: - if scope is _Unset: - raise TypeError("missing required argument: scope") - body = { - "items": items, - "previous_update_id": previous_update_id, - "scope": scope, - "update_delay": update_delay, - } - body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None - if isinstance(body, (IOBase, bytes)): - _content = body + if isinstance(credential_request, (IOBase, bytes)): + _content = credential_request else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(credential_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_memory_stores_update_memories_request( + _request = build_beta_evaluators_get_credentials_request( name=name, + version=version, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -6161,18 +6853,19 @@ async def _update_memories_initial( _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = True + _stream = kwargs.pop("stream", False) pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - if response.status_code not in [202]: - try: - await response.read() # Load the body in memory and close the socket - except (StreamConsumedError, StreamClosedError): - pass + 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, @@ -6180,210 +6873,4834 @@ async def _update_memories_initial( ) raise HttpResponseError(response=response, model=error) - response_headers = {} - response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) - - deserialized = response.iter_bytes() if _decompress else response.iter_raw() + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DatasetCredential, response.json()) if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore + return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore @overload - async def _begin_update_memories( + async def create_generation_job( self, - name: str, + job: _models.EvaluatorGenerationJob, *, - scope: str, + operation_id: Optional[str] = None, content_type: str = "application/json", - items: Optional[List[dict[str, Any]]] = None, - previous_update_id: Optional[str] = None, - update_delay: Optional[int] = None, **kwargs: Any - ) -> AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult]: ... - @overload - async def _begin_update_memories( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult]: ... - @overload - async def _begin_update_memories( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult]: ... + ) -> _models.EvaluatorGenerationJob: + """Creates an evaluator generation job. - @distributed_trace_async - async def _begin_update_memories( - self, - name: str, - body: Union[JSON, IO[bytes]] = _Unset, - *, - scope: str = _Unset, - items: Optional[List[dict[str, Any]]] = None, + Creates an evaluator generation job. The service generates rubric-based evaluator definitions + from the provided source materials asynchronously. + + :param job: The job to create. Required. + :type job: ~azure.ai.projects.models.EvaluatorGenerationJob + :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 + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + Creates an evaluator generation job. The service generates rubric-based evaluator definitions + from the provided source materials asynchronously. + + :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 + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_generation_job( + self, + job: IO[bytes], + *, + operation_id: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.EvaluatorGenerationJob: + """Creates an evaluator generation job. + + Creates an evaluator generation job. The service generates rubric-based evaluator definitions + from the provided source materials asynchronously. + + :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 + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def create_generation_job( + self, + job: Union[_models.EvaluatorGenerationJob, JSON, IO[bytes]], + *, + operation_id: Optional[str] = None, + **kwargs: Any + ) -> _models.EvaluatorGenerationJob: + """Creates an evaluator generation job. + + Creates an evaluator generation job. The service generates rubric-based evaluator definitions + from the provided source materials asynchronously. + + :param job: The job to create. Is one of the following types: EvaluatorGenerationJob, JSON, + IO[bytes] Required. + :type job: ~azure.ai.projects.models.EvaluatorGenerationJob 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 + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.EvaluatorGenerationJob] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(job, (IOBase, bytes)): + _content = job + else: + _content = json.dumps(job, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_evaluators_create_generation_job_request( + operation_id=operation_id, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [201]: + 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, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) + response_headers["Location"] = self._deserialize("str", response.headers.get("Location")) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.EvaluatorGenerationJob, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def get_generation_job(self, job_id: str, **kwargs: Any) -> _models.EvaluatorGenerationJob: + """Get info about an evaluator generation job. + + Gets the details of an evaluator generation job by its ID. + + :param job_id: The ID of the job. Required. + :type job_id: str + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.EvaluatorGenerationJob] = kwargs.pop("cls", None) + + _request = build_beta_evaluators_get_generation_job_request( + job_id=job_id, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Retry-After"] = self._deserialize("int", response.headers.get("Retry-After")) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.EvaluatorGenerationJob, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_generation_jobs( + self, + *, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.EvaluatorGenerationJob"]: + """Returns a list of evaluator generation jobs. + + Returns a list of evaluator generation jobs. + + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of EvaluatorGenerationJob + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.EvaluatorGenerationJob] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.EvaluatorGenerationJob]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_beta_evaluators_list_generation_jobs_request( + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.EvaluatorGenerationJob], + 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) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace_async + async def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.EvaluatorGenerationJob: + """Cancels an evaluator generation job. + + Cancels an evaluator generation job by its ID. + + :param job_id: The ID of the job to cancel. Required. + :type job_id: str + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.EvaluatorGenerationJob] = kwargs.pop("cls", None) + + _request = build_beta_evaluators_cancel_generation_job_request( + job_id=job_id, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.EvaluatorGenerationJob, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @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 + evaluator (if any) is preserved. + + :param job_id: The ID of the job to delete. Required. + :type job_id: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_evaluators_delete_generation_job_request( + job_id=job_id, + 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) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + 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, None, {}) # type: ignore + + +class BetaInsightsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.aio.AIProjectClient`'s + :attr:`insights` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @overload + async def generate( + self, insight: _models.Insight, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.Insight: + """Generate Insights. + + :param insight: Complete evaluation configuration including data source, evaluators, and result + settings. Required. + :type insight: ~azure.ai.projects.models.Insight + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: Insight. The Insight is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Insight + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def generate( + self, insight: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.Insight: + """Generate Insights. + + :param insight: Complete evaluation configuration including data source, evaluators, and result + settings. Required. + :type insight: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: Insight. The Insight is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Insight + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def generate( + self, insight: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.Insight: + """Generate Insights. + + :param insight: Complete evaluation configuration including data source, evaluators, and result + settings. Required. + :type insight: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: Insight. The Insight is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Insight + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwargs: Any) -> _models.Insight: + """Generate Insights. + + :param insight: Complete evaluation configuration including data source, evaluators, and result + settings. Is one of the following types: Insight, JSON, IO[bytes] Required. + :type insight: ~azure.ai.projects.models.Insight or JSON or IO[bytes] + :return: Insight. The Insight is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Insight + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.Insight] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(insight, (IOBase, bytes)): + _content = insight + else: + _content = json.dumps(insight, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_insights_generate_request( + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [201]: + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Insight, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def get( + self, insight_id: str, *, include_coordinates: Optional[bool] = None, **kwargs: Any + ) -> _models.Insight: + """Get a specific insight by Id. + + :param insight_id: The unique identifier for the insights report. Required. + :type insight_id: str + :keyword include_coordinates: Whether to include coordinates for visualization in the response. + Defaults to false. Default value is None. + :paramtype include_coordinates: bool + :return: Insight. The Insight is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Insight + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.Insight] = kwargs.pop("cls", None) + + _request = build_beta_insights_get_request( + insight_id=insight_id, + include_coordinates=include_coordinates, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Insight, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list( + self, + *, + type: Optional[Union[str, _models.InsightType]] = None, + eval_id: Optional[str] = None, + run_id: Optional[str] = None, + agent_name: Optional[str] = None, + include_coordinates: Optional[bool] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.Insight"]: + """List all insights in reverse chronological order (newest first). + + :keyword type: Filter by the type of analysis. Known values are: "EvaluationRunClusterInsight", + "AgentClusterInsight", and "EvaluationComparison". Default value is None. + :paramtype type: str or ~azure.ai.projects.models.InsightType + :keyword eval_id: Filter by the evaluation ID. Default value is None. + :paramtype eval_id: str + :keyword run_id: Filter by the evaluation run ID. Default value is None. + :paramtype run_id: str + :keyword agent_name: Filter by the agent name. Default value is None. + :paramtype agent_name: str + :keyword include_coordinates: Whether to include coordinates for visualization in the response. + Defaults to false. Default value is None. + :paramtype include_coordinates: bool + :return: An iterator like instance of Insight + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.Insight] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.Insight]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_beta_insights_list_request( + type=type, + eval_id=eval_id, + run_id=run_id, + agent_name=agent_name, + include_coordinates=include_coordinates, + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.Insight], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + +class BetaMemoryStoresOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.aio.AIProjectClient`'s + :attr:`memory_stores` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @overload + async def create( + self, + *, + name: str, + definition: _models.MemoryStoreDefinition, + content_type: str = "application/json", + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, + **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Create a memory store. + + :keyword name: The name of the memory store. Required. + :paramtype name: str + :keyword definition: The memory store definition. Required. + :paramtype definition: ~azure.ai.projects.models.MemoryStoreDefinition + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword description: A human-readable description of the memory store. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default + value is None. + :paramtype metadata: dict[str, str] + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create( + self, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Create a memory store. + + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create( + self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Create a memory store. + + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def create( + self, + body: Union[JSON, IO[bytes]] = _Unset, + *, + name: str = _Unset, + definition: _models.MemoryStoreDefinition = _Unset, + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, + **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Create a memory store. + + :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. + :paramtype name: str + :keyword definition: The memory store definition. Required. + :paramtype definition: ~azure.ai.projects.models.MemoryStoreDefinition + :keyword description: A human-readable description of the memory store. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default + value is None. + :paramtype metadata: dict[str, str] + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) + + if body is _Unset: + if name is _Unset: + raise TypeError("missing required argument: name") + if definition is _Unset: + raise TypeError("missing required argument: definition") + body = {"definition": definition, "description": description, "metadata": metadata, "name": name} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_memory_stores_create_request( + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + async def update( + self, + name: str, + *, + content_type: str = "application/json", + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, + **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Update a memory store. + + :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. + Default value is "application/json". + :paramtype content_type: str + :keyword description: A human-readable description of the memory store. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default + value is None. + :paramtype metadata: dict[str, str] + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update( + self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Update a memory store. + + :param name: The name of the memory store to update. Required. + :type name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update( + self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Update a memory store. + + :param name: The name of the memory store to update. Required. + :type name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def update( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, + **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Update a memory store. + + :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. + :type body: JSON or IO[bytes] + :keyword description: A human-readable description of the memory store. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default + value is None. + :paramtype metadata: dict[str, str] + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) + + if body is _Unset: + body = {"description": description, "metadata": metadata} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_memory_stores_update_request( + name=name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def get(self, name: str, **kwargs: Any) -> _models.MemoryStoreDetails: + """Retrieve a memory store. + + :param name: The name of the memory store to retrieve. Required. + :type name: str + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) + + _request = build_beta_memory_stores_get_request( + name=name, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list( + self, + *, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.MemoryStoreDetails"]: + """List all memory stores. + + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of MemoryStoreDetails + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.MemoryStoreDetails] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.MemoryStoreDetails]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_beta_memory_stores_list_request( + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.MemoryStoreDetails], + 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) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace_async + async def delete(self, name: str, **kwargs: Any) -> _models.DeleteMemoryStoreResult: + """Delete a memory store. + + :param name: The name of the memory store to delete. Required. + :type name: str + :return: DeleteMemoryStoreResult. The DeleteMemoryStoreResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DeleteMemoryStoreResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.DeleteMemoryStoreResult] = kwargs.pop("cls", None) + + _request = build_beta_memory_stores_delete_request( + name=name, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DeleteMemoryStoreResult, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + async def _search_memories( + self, + name: str, + *, + scope: str, + content_type: str = "application/json", + items: Optional[List[dict[str, Any]]] = None, + previous_search_id: Optional[str] = None, + options: Optional[_models.MemorySearchOptions] = None, + **kwargs: Any + ) -> _models.MemoryStoreSearchResult: ... + @overload + async def _search_memories( + self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreSearchResult: ... + @overload + async def _search_memories( + self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreSearchResult: ... + + @distributed_trace_async + async def _search_memories( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + scope: str = _Unset, + items: Optional[List[dict[str, Any]]] = None, + previous_search_id: Optional[str] = None, + options: Optional[_models.MemorySearchOptions] = None, + **kwargs: Any + ) -> _models.MemoryStoreSearchResult: + """Search for relevant memories from a memory store based on conversation context. + + :param name: The name of the memory store to search. Required. + :type name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :paramtype scope: str + :keyword items: Items for which to search for relevant memories. Default value is None. + :paramtype items: list[dict[str, any]] + :keyword previous_search_id: The unique ID of the previous search request, enabling incremental + memory search from where the last operation left off. Default value is None. + :paramtype previous_search_id: str + :keyword options: Memory search options. Default value is None. + :paramtype options: ~azure.ai.projects.models.MemorySearchOptions + :return: MemoryStoreSearchResult. The MemoryStoreSearchResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreSearchResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryStoreSearchResult] = kwargs.pop("cls", None) + + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = { + "items": items, + "options": options, + "previous_search_id": previous_search_id, + "scope": scope, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_memory_stores_search_memories_request( + name=name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.MemoryStoreSearchResult, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + async def _update_memories_initial( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + scope: str = _Unset, + items: Optional[List[dict[str, Any]]] = None, + previous_update_id: Optional[str] = None, + update_delay: Optional[int] = None, + **kwargs: Any + ) -> AsyncIterator[bytes]: + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None) + + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = { + "items": items, + "previous_update_id": previous_update_id, + "scope": scope, + "update_delay": update_delay, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_memory_stores_update_memories_request( + name=name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _decompress = kwargs.pop("decompress", True) + _stream = True + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [202]: + 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, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @overload + async def _begin_update_memories( + self, + name: str, + *, + scope: str, + content_type: str = "application/json", + items: Optional[List[dict[str, Any]]] = None, + previous_update_id: Optional[str] = None, + update_delay: Optional[int] = None, + **kwargs: Any + ) -> AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult]: ... + @overload + async def _begin_update_memories( + self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult]: ... + @overload + async def _begin_update_memories( + self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult]: ... + + @distributed_trace_async + async def _begin_update_memories( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + scope: str = _Unset, + items: Optional[List[dict[str, Any]]] = None, previous_update_id: Optional[str] = None, update_delay: Optional[int] = None, **kwargs: Any - ) -> AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult]: - """Update memory store with conversation memories. + ) -> AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult]: + """Update memory store with conversation memories. + + :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. + :type body: JSON or IO[bytes] + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :paramtype scope: str + :keyword items: Conversation items to be stored in memory. Default value is None. + :paramtype items: list[dict[str, any]] + :keyword previous_update_id: The unique ID of the previous update request, enabling incremental + memory updates from where the last operation left off. Default value is None. + :paramtype previous_update_id: str + :keyword update_delay: Timeout period before processing the memory update in seconds. + If a new update request is received during this period, it will cancel the current request and + reset the timeout. + Set to 0 to immediately trigger the update without delay. + Defaults to 300 (5 minutes). Default value is None. + :paramtype update_delay: int + :return: An instance of AsyncLROPoller that returns MemoryStoreUpdateCompletedResult. The + MemoryStoreUpdateCompletedResult is compatible with MutableMapping + :rtype: + ~azure.core.polling.AsyncLROPoller[~azure.ai.projects.models.MemoryStoreUpdateCompletedResult] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryStoreUpdateCompletedResult] = kwargs.pop("cls", None) + polling: Union[bool, AsyncPollingMethod] = kwargs.pop("polling", True) + lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) + cont_token: Optional[str] = kwargs.pop("continuation_token", None) + if cont_token is None: + raw_result = await self._update_memories_initial( + name=name, + body=body, + scope=scope, + items=items, + previous_update_id=previous_update_id, + update_delay=update_delay, + content_type=content_type, + cls=lambda x, y, z: x, + headers=_headers, + params=_params, + **kwargs + ) + await raw_result.http_response.read() # type: ignore + kwargs.pop("error_map", None) + + def get_long_running_output(pipeline_response): + response_headers = {} + response = pipeline_response.http_response + response_headers["Operation-Location"] = self._deserialize( + "str", response.headers.get("Operation-Location") + ) + + deserialized = _deserialize(_models.MemoryStoreUpdateCompletedResult, response.json().get("result", {})) + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + return deserialized + + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + + if polling is True: + polling_method: AsyncPollingMethod = cast( + AsyncPollingMethod, + AsyncLROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs), + ) + elif polling is False: + polling_method = cast(AsyncPollingMethod, AsyncNoPolling()) + else: + polling_method = polling + if cont_token: + return AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult].from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output, + ) + return AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult]( + self._client, raw_result, get_long_running_output, polling_method # type: ignore + ) + + @overload + 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. + + :param name: The name of the memory store. Required. + :type name: str + :keyword scope: The namespace that logically groups and isolates memories to delete, such as a + user ID. Required. + :paramtype scope: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param name: The name of the memory store. Required. + :type name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param name: The name of the memory store. Required. + :type name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + 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. + + :param name: The name of the memory store. Required. + :type name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword scope: The namespace that logically groups and isolates memories to delete, such as a + user ID. Required. + :paramtype scope: str + :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryStoreDeleteScopeResult] = kwargs.pop("cls", None) + + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = {"scope": scope} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_memory_stores_delete_scope_request( + name=name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.MemoryStoreDeleteScopeResult, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + async def create_memory( + self, + name: str, + *, + scope: str, + content: str, + kind: Union[str, _models.MemoryItemKind], + content_type: str = "application/json", + **kwargs: Any + ) -> _models.MemoryItem: + """Create a memory item in a memory store. + + :param name: The name of the memory store. Required. + :type name: str + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :paramtype scope: str + :keyword content: The content of the memory. Required. + :paramtype content: str + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Required. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param name: The name of the memory store. Required. + :type name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param name: The name of the memory store. Required. + :type name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def create_memory( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + scope: str = _Unset, + content: str = _Unset, + kind: Union[str, _models.MemoryItemKind] = _Unset, + **kwargs: Any + ) -> _models.MemoryItem: + """Create a memory item in a memory store. + + :param name: The name of the memory store. Required. + :type name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :paramtype scope: str + :keyword content: The content of the memory. Required. + :paramtype content: str + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Required. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryItem] = kwargs.pop("cls", None) + + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + if content is _Unset: + raise TypeError("missing required argument: content") + if kind is _Unset: + raise TypeError("missing required argument: kind") + body = {"content": content, "kind": kind, "scope": scope} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_memory_stores_create_memory_request( + name=name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.MemoryItem, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + 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. + + :param name: The name of the memory store. Required. + :type name: str + :param memory_id: The ID of the memory item to update. Required. + :type memory_id: str + :keyword content: The updated content of the memory. Required. + :paramtype content: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param name: The name of the memory store. Required. + :type name: str + :param memory_id: The ID of the memory item to update. Required. + :type memory_id: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param name: The name of the memory store. Required. + :type name: str + :param memory_id: The ID of the memory item to update. Required. + :type memory_id: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + 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. + + :param name: The name of the memory store. Required. + :type name: str + :param memory_id: The ID of the memory item to update. Required. + :type memory_id: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword content: The updated content of the memory. Required. + :paramtype content: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryItem] = kwargs.pop("cls", None) + + if body is _Unset: + if content is _Unset: + raise TypeError("missing required argument: content") + body = {"content": content} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_memory_stores_update_memory_request( + name=name, + memory_id=memory_id, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.MemoryItem, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @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. + + :param name: The name of the memory store. Required. + :type name: str + :param memory_id: The ID of the memory item to retrieve. Required. + :type memory_id: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.MemoryItem] = kwargs.pop("cls", None) + + _request = build_beta_memory_stores_get_memory_request( + name=name, + memory_id=memory_id, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.MemoryItem, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + def list_memories( + self, + name: str, + *, + scope: str, + kind: Optional[Union[str, _models.MemoryItemKind]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> AsyncItemPaged["_models.MemoryItem"]: + """List all memory items in a memory store. + + :param name: The name of the memory store. Required. + :type name: str + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :paramtype scope: str + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Default value is None. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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 + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: An iterator like instance of MemoryItem + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.MemoryItem] + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def list_memories( + self, + name: str, + body: JSON, + *, + kind: Optional[Union[str, _models.MemoryItemKind]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> AsyncItemPaged["_models.MemoryItem"]: + """List all memory items in a memory store. + + :param name: The name of the memory store. Required. + :type name: str + :param body: Required. + :type body: JSON + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Default value is None. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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 + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: An iterator like instance of MemoryItem + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.MemoryItem] + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def list_memories( + self, + name: str, + body: IO[bytes], + *, + kind: Optional[Union[str, _models.MemoryItemKind]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> AsyncItemPaged["_models.MemoryItem"]: + """List all memory items in a memory store. + + :param name: The name of the memory store. Required. + :type name: str + :param body: Required. + :type body: IO[bytes] + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Default value is None. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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 + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: An iterator like instance of MemoryItem + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.MemoryItem] + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def list_memories( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + scope: str = _Unset, + kind: Optional[Union[str, _models.MemoryItemKind]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.MemoryItem"]: + """List all memory items in a memory store. + + :param name: The name of the memory store. Required. + :type name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :paramtype scope: str + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Default value is None. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of MemoryItem + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.MemoryItem] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[List[_models.MemoryItem]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + def prepare_request(_continuation_token=None): + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = {"scope": scope} + body = {k: v for k, v in body.items() if v is not None} + + _request = build_beta_memory_stores_list_memories_request( + name=name, + kind=kind, + limit=limit, + order=order, + after=_continuation_token, + before=before, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.MemoryItem], + 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) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @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. + + :param name: The name of the memory store. Required. + :type name: str + :param memory_id: The ID of the memory item to delete. Required. + :type memory_id: str + :return: DeleteMemoryResult. The DeleteMemoryResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DeleteMemoryResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.DeleteMemoryResult] = kwargs.pop("cls", None) + + _request = build_beta_memory_stores_delete_memory_request( + name=name, + memory_id=memory_id, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DeleteMemoryResult, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + +class BetaModelsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.aio.AIProjectClient`'s + :attr:`models` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @distributed_trace + def list_versions(self, name: str, **kwargs: Any) -> AsyncItemPaged["_models.ModelVersion"]: + """List all versions of the given ModelVersion. + + :param name: The name of the resource. Required. + :type name: str + :return: An iterator like instance of ModelVersion + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.ModelVersion] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.ModelVersion]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_beta_models_list_versions_request( + name=name, + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.ModelVersion], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace + def list(self, **kwargs: Any) -> AsyncItemPaged["_models.ModelVersion"]: + """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] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.ModelVersion]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_beta_models_list_request( + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.ModelVersion], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @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. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the ModelVersion to retrieve. Required. + :type version: str + :return: ModelVersion. The ModelVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ModelVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.ModelVersion] = kwargs.pop("cls", None) + + _request = build_beta_models_get_request( + name=name, + version=version, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.ModelVersion, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @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 + was deleted successfully or if the ModelVersion does not exist. + + :param name: The name of the resource. Required. + :type name: str + :param version: The version of the ModelVersion to delete. Required. + :type version: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_models_delete_request( + name=name, + version=version, + 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) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if cls: + return cls(pipeline_response, None, {}) # type: ignore + + @overload + async def update( + self, + name: str, + version: str, + body: _models.UpdateModelVersionRequest, + *, + content_type: str = "application/merge-patch+json", + **kwargs: Any + ) -> _models.ModelVersion: + """Update an existing ModelVersion with the given version id. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the UpdateModelVersionRequest to create or update. + Required. + :type version: str + :param body: The UpdateModelVersionRequest to create or update. Required. + :type body: ~azure.ai.projects.models.UpdateModelVersionRequest + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :return: ModelVersion. The ModelVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ModelVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update( + self, name: str, version: str, body: JSON, *, content_type: str = "application/merge-patch+json", **kwargs: Any + ) -> _models.ModelVersion: + """Update an existing ModelVersion with the given version id. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the UpdateModelVersionRequest to create or update. + Required. + :type version: str + :param body: The UpdateModelVersionRequest to create or update. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :return: ModelVersion. The ModelVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ModelVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update( + self, + name: str, + version: str, + body: IO[bytes], + *, + content_type: str = "application/merge-patch+json", + **kwargs: Any + ) -> _models.ModelVersion: + """Update an existing ModelVersion with the given version id. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the UpdateModelVersionRequest to create or update. + Required. + :type version: str + :param body: The UpdateModelVersionRequest to create or update. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :return: ModelVersion. The ModelVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ModelVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def update( + self, name: str, version: str, body: Union[_models.UpdateModelVersionRequest, JSON, IO[bytes]], **kwargs: Any + ) -> _models.ModelVersion: + """Update an existing ModelVersion with the given version id. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the UpdateModelVersionRequest to create or update. + Required. + :type version: str + :param body: The UpdateModelVersionRequest to create or update. Is one of the following types: + UpdateModelVersionRequest, JSON, IO[bytes] Required. + :type body: ~azure.ai.projects.models.UpdateModelVersionRequest or JSON or IO[bytes] + :return: ModelVersion. The ModelVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ModelVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.ModelVersion] = kwargs.pop("cls", None) + + content_type = content_type or "application/merge-patch+json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_models_update_request( + name=name, + version=version, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 201]: + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.ModelVersion, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + async def pending_create_version( + self, + name: str, + version: str, + model_version: _models.ModelVersion, + *, + 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. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param model_version: Model version to create. Required. + :type model_version: ~azure.ai.projects.models.ModelVersion + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: CreateAsyncResponse. The CreateAsyncResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CreateAsyncResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param model_version: Model version to create. Required. + :type model_version: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: CreateAsyncResponse. The CreateAsyncResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CreateAsyncResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def pending_create_version( + self, + name: str, + version: str, + model_version: IO[bytes], + *, + 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. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param model_version: Model version to create. Required. + :type model_version: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: CreateAsyncResponse. The CreateAsyncResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CreateAsyncResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + 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. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param model_version: Model version to create. Is one of the following types: ModelVersion, + JSON, IO[bytes] Required. + :type model_version: ~azure.ai.projects.models.ModelVersion or JSON or IO[bytes] + :return: CreateAsyncResponse. The CreateAsyncResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CreateAsyncResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.CreateAsyncResponse] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(model_version, (IOBase, bytes)): + _content = model_version + else: + _content = json.dumps(model_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_models_pending_create_version_request( + name=name, + version=version, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [202]: + 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) + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["Location"] = self._deserialize("str", response.headers.get("Location")) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.CreateAsyncResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @overload + async def pending_upload( + self, + name: str, + version: str, + pending_upload_request: _models.ModelPendingUploadRequest, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.ModelPendingUploadResponse: + """Start or retrieve a pending upload for a model version. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param pending_upload_request: Required. + :type pending_upload_request: ~azure.ai.projects.models.ModelPendingUploadRequest + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: ModelPendingUploadResponse. The ModelPendingUploadResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.ModelPendingUploadResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def pending_upload( + self, + name: str, + version: str, + pending_upload_request: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.ModelPendingUploadResponse: + """Start or retrieve a pending upload for a model version. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param pending_upload_request: Required. + :type pending_upload_request: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: ModelPendingUploadResponse. The ModelPendingUploadResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.ModelPendingUploadResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def pending_upload( + self, + name: str, + version: str, + pending_upload_request: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.ModelPendingUploadResponse: + """Start or retrieve a pending upload for a model version. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param pending_upload_request: Required. + :type pending_upload_request: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: ModelPendingUploadResponse. The ModelPendingUploadResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.ModelPendingUploadResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def pending_upload( + self, + name: str, + version: str, + pending_upload_request: Union[_models.ModelPendingUploadRequest, JSON, IO[bytes]], + **kwargs: Any + ) -> _models.ModelPendingUploadResponse: + """Start or retrieve a pending upload for a model version. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param pending_upload_request: Is one of the following types: ModelPendingUploadRequest, JSON, + IO[bytes] Required. + :type pending_upload_request: ~azure.ai.projects.models.ModelPendingUploadRequest or JSON or + IO[bytes] + :return: ModelPendingUploadResponse. The ModelPendingUploadResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.ModelPendingUploadResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.ModelPendingUploadResponse] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(pending_upload_request, (IOBase, bytes)): + _content = pending_upload_request + else: + _content = json.dumps(pending_upload_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_models_pending_upload_request( + name=name, + version=version, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.ModelPendingUploadResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + async def get_credentials( + self, + name: str, + version: str, + credential_request: _models.ModelCredentialRequest, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.DatasetCredential: + """Get credentials for a model version asset. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param credential_request: Required. + :type credential_request: ~azure.ai.projects.models.ModelCredentialRequest + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def get_credentials( + self, + name: str, + version: str, + credential_request: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.DatasetCredential: + """Get credentials for a model version asset. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param credential_request: Required. + :type credential_request: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def get_credentials( + self, + name: str, + version: str, + credential_request: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.DatasetCredential: + """Get credentials for a model version asset. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param credential_request: Required. + :type credential_request: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def get_credentials( + self, + name: str, + version: str, + credential_request: Union[_models.ModelCredentialRequest, JSON, IO[bytes]], + **kwargs: Any + ) -> _models.DatasetCredential: + """Get credentials for a model version asset. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param credential_request: Is one of the following types: ModelCredentialRequest, JSON, + IO[bytes] Required. + :type credential_request: ~azure.ai.projects.models.ModelCredentialRequest or JSON or IO[bytes] + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.DatasetCredential] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(credential_request, (IOBase, bytes)): + _content = credential_request + else: + _content = json.dumps(credential_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_models_get_credentials_request( + name=name, + version=version, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DatasetCredential, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + +class BetaRedTeamsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.aio.AIProjectClient`'s + :attr:`red_teams` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @distributed_trace_async + async def get(self, name: str, **kwargs: Any) -> _models.RedTeam: + """Get a redteam by name. + + :param name: Identifier of the red team run. Required. + :type name: str + :return: RedTeam. The RedTeam is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.RedTeam + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.RedTeam] = kwargs.pop("cls", None) + + _request = build_beta_red_teams_get_request( + name=name, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.RedTeam, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list(self, **kwargs: Any) -> AsyncItemPaged["_models.RedTeam"]: + """List a redteam by name. + + :return: An iterator like instance of RedTeam + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.RedTeam] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.RedTeam]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_beta_red_teams_list_request( + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.RedTeam], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @overload + async def create( + self, red_team: _models.RedTeam, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.RedTeam: + """Creates a redteam run. + + :param red_team: Redteam to be run. Required. + :type red_team: ~azure.ai.projects.models.RedTeam + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: RedTeam. The RedTeam is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.RedTeam + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create(self, red_team: JSON, *, content_type: str = "application/json", **kwargs: Any) -> _models.RedTeam: + """Creates a redteam run. + + :param red_team: Redteam to be run. Required. + :type red_team: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: RedTeam. The RedTeam is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.RedTeam + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create( + self, red_team: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.RedTeam: + """Creates a redteam run. + + :param red_team: Redteam to be run. Required. + :type red_team: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: RedTeam. The RedTeam is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.RedTeam + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwargs: Any) -> _models.RedTeam: + """Creates a redteam run. + + :param red_team: Redteam to be run. Is one of the following types: RedTeam, JSON, IO[bytes] + Required. + :type red_team: ~azure.ai.projects.models.RedTeam or JSON or IO[bytes] + :return: RedTeam. The RedTeam is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.RedTeam + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.RedTeam] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(red_team, (IOBase, bytes)): + _content = red_team + else: + _content = json.dumps(red_team, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_red_teams_create_request( + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [201]: + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.RedTeam, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + +class BetaRoutinesOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.aio.AIProjectClient`'s + :attr:`routines` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @overload + async def create_or_update( + self, + routine_name: str, + *, + content_type: str = "application/json", + description: Optional[str] = None, + enabled: Optional[bool] = None, + triggers: Optional[dict[str, _models.RoutineTrigger]] = None, + action: Optional[_models.RoutineAction] = None, + **kwargs: Any + ) -> _models.Routine: + """Create or update a 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. + Default value is "application/json". + :paramtype content_type: str + :keyword description: A human-readable description of the routine. Default value is None. + :paramtype description: str + :keyword enabled: Whether the routine is enabled. Default value is None. + :paramtype enabled: bool + :keyword triggers: The triggers configured for the routine. In v1, exactly one trigger entry is + supported. Default value is None. + :paramtype triggers: dict[str, ~azure.ai.projects.models.RoutineTrigger] + :keyword action: The action executed when the routine fires. Default value is None. + :paramtype action: ~azure.ai.projects.models.RoutineAction + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_or_update( + self, routine_name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.Routine: + """Create or update a routine. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_or_update( + self, routine_name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.Routine: + """Create or update a routine. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def create_or_update( + self, + routine_name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + description: Optional[str] = None, + enabled: Optional[bool] = None, + triggers: Optional[dict[str, _models.RoutineTrigger]] = None, + action: Optional[_models.RoutineAction] = None, + **kwargs: Any + ) -> _models.Routine: + """Create or update a 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. + :type body: JSON or IO[bytes] + :keyword description: A human-readable description of the routine. Default value is None. + :paramtype description: str + :keyword enabled: Whether the routine is enabled. Default value is None. + :paramtype enabled: bool + :keyword triggers: The triggers configured for the routine. In v1, exactly one trigger entry is + supported. Default value is None. + :paramtype triggers: dict[str, ~azure.ai.projects.models.RoutineTrigger] + :keyword action: The action executed when the routine fires. Default value is None. + :paramtype action: ~azure.ai.projects.models.RoutineAction + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.Routine] = kwargs.pop("cls", None) + + if body is _Unset: + body = {"action": action, "description": description, "enabled": enabled, "triggers": triggers} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_routines_create_or_update_request( + routine_name=routine_name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Routine, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def get(self, routine_name: str, **kwargs: Any) -> _models.Routine: + """Retrieve a routine. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.Routine] = kwargs.pop("cls", None) + + _request = build_beta_routines_get_request( + routine_name=routine_name, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Routine, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def enable(self, routine_name: str, **kwargs: Any) -> _models.Routine: + """Enable a routine. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.Routine] = kwargs.pop("cls", None) + + _request = build_beta_routines_enable_request( + routine_name=routine_name, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Routine, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def disable(self, routine_name: str, **kwargs: Any) -> _models.Routine: + """Disable a routine. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.Routine] = kwargs.pop("cls", None) + + _request = build_beta_routines_disable_request( + routine_name=routine_name, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Routine, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list( + self, *, limit: Optional[int] = None, before: Optional[str] = None, order: Optional[str] = None, **kwargs: Any + ) -> AsyncItemPaged["_models.Routine"]: + """List routines. + + :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 + None. + :paramtype before: str + :keyword order: The ordering direction. Supported values are asc and desc. Default value is + None. + :paramtype order: str + :return: An iterator like instance of Routine + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.Routine] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.Routine]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_beta_routines_list_request( + limit=limit, + after=_continuation_token, + before=before, + order=order, + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.Routine], + 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) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace_async + async def delete(self, routine_name: str, **kwargs: Any) -> None: + """Delete a routine. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_routines_delete_request( + routine_name=routine_name, + 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) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + 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, None, {}) # type: ignore + + @distributed_trace + def list_runs( + self, + routine_name: str, + *, + filter: Optional[str] = None, + limit: Optional[int] = None, + before: Optional[str] = None, + order: Optional[str] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.RoutineRun"]: + """List prior runs for a 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 + experiment. Default value is None. + :paramtype filter: str + :keyword limit: The maximum number of runs to return. Default value is None. + :paramtype limit: int + :keyword before: Unsupported. Reserved for future backward pagination support. Default value is + None. + :paramtype before: str + :keyword order: The ordering direction. Supported values are asc and desc. Default value is + None. + :paramtype order: str + :return: An iterator like instance of RoutineRun + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.RoutineRun] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.RoutineRun]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_beta_routines_list_runs_request( + routine_name=routine_name, + filter=filter, + limit=limit, + after=_continuation_token, + before=before, + order=order, + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.RoutineRun], + 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) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @overload + async def dispatch( + self, + routine_name: str, + *, + content_type: str = "application/json", + payload: Optional[_models.RoutineDispatchPayload] = None, + **kwargs: Any + ) -> _models.DispatchRoutineResult: + """Queue an asynchronous routine dispatch. + + :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. + Default value is "application/json". + :paramtype content_type: str + :keyword payload: A direct action-input override sent downstream when testing a routine. + Default value is None. + :paramtype payload: ~azure.ai.projects.models.RoutineDispatchPayload + :return: DispatchRoutineResult. The DispatchRoutineResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DispatchRoutineResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def dispatch( + self, routine_name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.DispatchRoutineResult: + """Queue an asynchronous routine dispatch. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: DispatchRoutineResult. The DispatchRoutineResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DispatchRoutineResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def dispatch( + self, routine_name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.DispatchRoutineResult: + """Queue an asynchronous routine dispatch. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: DispatchRoutineResult. The DispatchRoutineResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DispatchRoutineResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def dispatch( + self, + routine_name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + payload: Optional[_models.RoutineDispatchPayload] = None, + **kwargs: Any + ) -> _models.DispatchRoutineResult: + """Queue an asynchronous routine dispatch. + + :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. + :type body: JSON or IO[bytes] + :keyword payload: A direct action-input override sent downstream when testing a routine. + Default value is None. + :paramtype payload: ~azure.ai.projects.models.RoutineDispatchPayload + :return: DispatchRoutineResult. The DispatchRoutineResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DispatchRoutineResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.DispatchRoutineResult] = kwargs.pop("cls", None) + + if body is _Unset: + body = {"payload": payload} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_routines_dispatch_request( + routine_name=routine_name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DispatchRoutineResult, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + +class BetaSchedulesOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.aio.AIProjectClient`'s + :attr:`schedules` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @distributed_trace_async + async def delete(self, schedule_id: str, **kwargs: Any) -> None: + """Delete a schedule. + + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_schedules_delete_request( + schedule_id=schedule_id, + 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) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if cls: + return cls(pipeline_response, None, {}) # type: ignore + + @distributed_trace_async + async def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: + """Get a schedule by id. + + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :return: Schedule. The Schedule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Schedule + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.Schedule] = kwargs.pop("cls", None) + + _request = build_beta_schedules_get_request( + schedule_id=schedule_id, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Schedule, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list( + self, + *, + type: Optional[Union[str, _models.ScheduleTaskType]] = None, + enabled: Optional[bool] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.Schedule"]: + """List all schedules. - :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. - :type body: JSON or IO[bytes] - :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. - Required. - :paramtype scope: str - :keyword items: Conversation items to be stored in memory. Default value is None. - :paramtype items: list[dict[str, any]] - :keyword previous_update_id: The unique ID of the previous update request, enabling incremental - memory updates from where the last operation left off. Default value is None. - :paramtype previous_update_id: str - :keyword update_delay: Timeout period before processing the memory update in seconds. - If a new update request is received during this period, it will cancel the current request and - reset the timeout. - Set to 0 to immediately trigger the update without delay. - Defaults to 300 (5 minutes). Default value is None. - :paramtype update_delay: int - :return: An instance of AsyncLROPoller that returns MemoryStoreUpdateCompletedResult. The - MemoryStoreUpdateCompletedResult is compatible with MutableMapping - :rtype: - ~azure.core.polling.AsyncLROPoller[~azure.ai.projects.models.MemoryStoreUpdateCompletedResult] + :keyword type: Filter by the type of schedule. Known values are: "Evaluation" and "Insight". + Default value is None. + :paramtype type: str or ~azure.ai.projects.models.ScheduleTaskType + :keyword enabled: Filter by the enabled status. Default value is None. + :paramtype enabled: bool + :return: An iterator like instance of Schedule + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.Schedule] :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.MemoryStoreUpdateCompletedResult] = kwargs.pop("cls", None) - polling: Union[bool, AsyncPollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = await self._update_memories_initial( - name=name, - body=body, - scope=scope, - items=items, - previous_update_id=previous_update_id, - update_delay=update_delay, - content_type=content_type, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - await raw_result.http_response.read() # type: ignore - kwargs.pop("error_map", None) + cls: ClsType[List[_models.Schedule]] = kwargs.pop("cls", None) - def get_long_running_output(pipeline_response): - response_headers = {} - response = pipeline_response.http_response - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) - deserialized = _deserialize(_models.MemoryStoreUpdateCompletedResult, response.json().get("result", {})) - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - return deserialized + def prepare_request(next_link=None): + if not next_link: - path_format_arguments = { - "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } + _request = build_beta_schedules_list_request( + type=type, + enabled=enabled, + 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) - if polling is True: - polling_method: AsyncPollingMethod = cast( - AsyncPollingMethod, - AsyncLROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs), + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.Schedule], + deserialized.get("value", []), ) - elif polling is False: - polling_method = cast(AsyncPollingMethod, AsyncNoPolling()) - else: - polling_method = polling - if cont_token: - return AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs ) - return AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult]( - self._client, raw_result, get_long_running_output, polling_method # type: ignore - ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) @overload - 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. + 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. - :param name: The name of the memory store. Required. - :type name: str - :keyword scope: The namespace that logically groups and isolates memories to delete, such as a - user ID. Required. - :paramtype scope: str + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :param schedule: The resource instance. Required. + :type schedule: ~azure.ai.projects.models.Schedule :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :return: Schedule. The Schedule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Schedule :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + 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. - :param name: The name of the memory store. Required. - :type name: str - :param body: Required. - :type body: JSON + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :param schedule: The resource instance. Required. + :type schedule: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :return: Schedule. The Schedule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Schedule :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + 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. - :param name: The name of the memory store. Required. - :type name: str - :param body: Required. - :type body: IO[bytes] + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :param schedule: The resource instance. Required. + :type schedule: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :return: Schedule. The Schedule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Schedule :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace_async - 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. + 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. - :param name: The name of the memory store. Required. - :type name: str - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword scope: The namespace that logically groups and isolates memories to delete, such as a - user ID. Required. - :paramtype scope: str - :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :param schedule: The resource instance. Is one of the following types: Schedule, JSON, + IO[bytes] Required. + :type schedule: ~azure.ai.projects.models.Schedule or JSON or IO[bytes] + :return: Schedule. The Schedule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Schedule :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -6398,22 +11715,17 @@ async def delete_scope( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.MemoryStoreDeleteScopeResult] = kwargs.pop("cls", None) + cls: ClsType[_models.Schedule] = kwargs.pop("cls", None) - if body is _Unset: - if scope is _Unset: - raise TypeError("missing required argument: scope") - body = {"scope": scope} - body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None - if isinstance(body, (IOBase, bytes)): - _content = body + if isinstance(schedule, (IOBase, bytes)): + _content = schedule else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(schedule, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_memory_stores_delete_scope_request( - name=name, + _request = build_beta_schedules_create_or_update_request( + schedule_id=schedule_id, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -6433,55 +11745,35 @@ async def delete_scope( response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [200, 201]: 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, - ) - raise HttpResponseError(response=response, model=error) + raise HttpResponseError(response=response) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.MemoryStoreDeleteScopeResult, response.json()) + deserialized = _deserialize(_models.Schedule, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - -class BetaRedTeamsOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.aio.AIProjectClient`'s - :attr:`red_teams` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - @distributed_trace_async - async def get(self, name: str, **kwargs: Any) -> _models.RedTeam: - """Get a redteam by name. + async def get_run(self, schedule_id: str, run_id: str, **kwargs: Any) -> _models.ScheduleRun: + """Get a schedule run by id. - :param name: Identifier of the red team run. Required. - :type name: str - :return: RedTeam. The RedTeam is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.RedTeam + :param schedule_id: The unique identifier of the schedule. Required. + :type schedule_id: str + :param run_id: The unique identifier of the schedule run. Required. + :type run_id: str + :return: ScheduleRun. The ScheduleRun is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ScheduleRun :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -6495,10 +11787,11 @@ async def get(self, name: str, **kwargs: Any) -> _models.RedTeam: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.RedTeam] = kwargs.pop("cls", None) + cls: ClsType[_models.ScheduleRun] = kwargs.pop("cls", None) - _request = build_beta_red_teams_get_request( - name=name, + _request = build_beta_schedules_get_run_request( + schedule_id=schedule_id, + run_id=run_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -6523,12 +11816,16 @@ async def get(self, name: str, **kwargs: Any) -> _models.RedTeam: except (StreamConsumedError, StreamClosedError): pass map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.RedTeam, response.json()) + deserialized = _deserialize(_models.ScheduleRun, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -6536,17 +11833,31 @@ async def get(self, name: str, **kwargs: Any) -> _models.RedTeam: return deserialized # type: ignore @distributed_trace - def list(self, **kwargs: Any) -> AsyncItemPaged["_models.RedTeam"]: - """List a redteam by name. + def list_runs( + self, + schedule_id: str, + *, + type: Optional[Union[str, _models.ScheduleTaskType]] = None, + enabled: Optional[bool] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.ScheduleRun"]: + """List all schedule runs. - :return: An iterator like instance of RedTeam - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.RedTeam] + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :keyword type: Filter by the type of schedule. Known values are: "Evaluation" and "Insight". + Default value is None. + :paramtype type: str or ~azure.ai.projects.models.ScheduleTaskType + :keyword enabled: Filter by the enabled status. Default value is None. + :paramtype enabled: bool + :return: An iterator like instance of ScheduleRun + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.ScheduleRun] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.RedTeam]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.ScheduleRun]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -6559,7 +11870,10 @@ def list(self, **kwargs: Any) -> AsyncItemPaged["_models.RedTeam"]: def prepare_request(next_link=None): if not next_link: - _request = build_beta_red_teams_list_request( + _request = build_beta_schedules_list_runs_request( + schedule_id=schedule_id, + type=type, + enabled=enabled, api_version=self._config.api_version, headers=_headers, params=_params, @@ -6599,7 +11913,7 @@ def prepare_request(next_link=None): async def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.RedTeam], + List[_models.ScheduleRun], deserialized.get("value", []), ) if cls: @@ -6623,61 +11937,136 @@ async def get_next(next_link=None): return AsyncItemPaged(get_next, extract_data) + +class BetaToolboxesOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.aio.AIProjectClient`'s + :attr:`toolboxes` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + @overload - async def create( - self, red_team: _models.RedTeam, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.RedTeam: - """Creates a redteam run. + async def create_version( + self, + name: str, + *, + tools: List[_models.Tool], + content_type: str = "application/json", + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, + skills: Optional[List[_models.ToolboxSkill]] = None, + 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. - :param red_team: Redteam to be run. Required. - :type red_team: ~azure.ai.projects.models.RedTeam + :param name: The name of the toolbox. If the toolbox does not exist, it will be created. + Required. + :type name: str + :keyword tools: The list of tools to include in this version. Required. + :paramtype tools: list[~azure.ai.projects.models.Tool] :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: RedTeam. The RedTeam is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.RedTeam + :keyword description: A human-readable description of the toolbox. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the toolbox. Default value is + None. + :paramtype metadata: dict[str, str] + :keyword skills: The list of skill sources to include in this version. A skill reference + specifies a skill name and optionally a version. If version is omitted, the skill's default + version is used. Default value is None. + :paramtype skills: list[~azure.ai.projects.models.ToolboxSkill] + :keyword policies: Policy configuration for this toolbox version. Default value is None. + :paramtype policies: ~azure.ai.projects.models.ToolboxPolicies + :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxVersionObject :raises ~azure.core.exceptions.HttpResponseError: """ @overload - async def create(self, red_team: JSON, *, content_type: str = "application/json", **kwargs: Any) -> _models.RedTeam: - """Creates a redteam run. + 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. - :param red_team: Redteam to be run. Required. - :type red_team: JSON + :param name: The name of the toolbox. If the toolbox does not exist, it will be created. + Required. + :type name: str + :param body: Required. + :type body: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: RedTeam. The RedTeam is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.RedTeam + :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxVersionObject :raises ~azure.core.exceptions.HttpResponseError: """ @overload - async def create( - self, red_team: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.RedTeam: - """Creates a redteam run. + 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. - :param red_team: Redteam to be run. Required. - :type red_team: IO[bytes] + :param name: The name of the toolbox. If the toolbox does not exist, it will be created. + Required. + :type name: str + :param body: Required. + :type body: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: RedTeam. The RedTeam is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.RedTeam + :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxVersionObject :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace_async - async def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwargs: Any) -> _models.RedTeam: - """Creates a redteam run. + async def create_version( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + tools: List[_models.Tool] = _Unset, + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, + skills: Optional[List[_models.ToolboxSkill]] = None, + 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. - :param red_team: Redteam to be run. Is one of the following types: RedTeam, JSON, IO[bytes] + :param name: The name of the toolbox. If the toolbox does not exist, it will be created. Required. - :type red_team: ~azure.ai.projects.models.RedTeam or JSON or IO[bytes] - :return: RedTeam. The RedTeam is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.RedTeam + :type name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword tools: The list of tools to include in this version. Required. + :paramtype tools: list[~azure.ai.projects.models.Tool] + :keyword description: A human-readable description of the toolbox. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the toolbox. Default value is + None. + :paramtype metadata: dict[str, str] + :keyword skills: The list of skill sources to include in this version. A skill reference + specifies a skill name and optionally a version. If version is omitted, the skill's default + version is used. Default value is None. + :paramtype skills: list[~azure.ai.projects.models.ToolboxSkill] + :keyword policies: Policy configuration for this toolbox version. Default value is None. + :paramtype policies: ~azure.ai.projects.models.ToolboxPolicies + :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxVersionObject :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -6692,16 +12081,28 @@ async def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwar _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.RedTeam] = kwargs.pop("cls", None) + cls: ClsType[_models.ToolboxVersionObject] = kwargs.pop("cls", None) + if body is _Unset: + if tools is _Unset: + raise TypeError("missing required argument: tools") + body = { + "description": description, + "metadata": metadata, + "policies": policies, + "skills": skills, + "tools": tools, + } + body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None - if isinstance(red_team, (IOBase, bytes)): - _content = red_team + if isinstance(body, (IOBase, bytes)): + _content = body else: - _content = json.dumps(red_team, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_red_teams_create_request( + _request = build_beta_toolboxes_create_version_request( + name=name, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -6721,7 +12122,7 @@ async def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwar response = pipeline_response.http_response - if response.status_code not in [201]: + if response.status_code not in [200]: if _stream: try: await response.read() # Load the body in memory and close the socket @@ -6737,87 +12138,21 @@ async def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwar if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.RedTeam, response.json()) + deserialized = _deserialize(_models.ToolboxVersionObject, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - -class BetaSchedulesOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.aio.AIProjectClient`'s - :attr:`schedules` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - - @distributed_trace_async - async def delete(self, schedule_id: str, **kwargs: Any) -> None: - """Delete a schedule. - - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[None] = kwargs.pop("cls", None) - - _request = build_beta_schedules_delete_request( - schedule_id=schedule_id, - 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) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [204]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if cls: - return cls(pipeline_response, None, {}) # type: ignore - @distributed_trace_async - async def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: - """Get a schedule by id. + async def get(self, name: str, **kwargs: Any) -> _models.ToolboxObject: + """Retrieve a toolbox. - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :return: Schedule. The Schedule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Schedule + :param name: The name of the toolbox to retrieve. Required. + :type name: str + :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxObject :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -6831,10 +12166,10 @@ async def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.Schedule] = kwargs.pop("cls", None) + cls: ClsType[_models.ToolboxObject] = kwargs.pop("cls", None) - _request = build_beta_schedules_get_request( - schedule_id=schedule_id, + _request = build_beta_toolboxes_get_request( + name=name, api_version=self._config.api_version, headers=_headers, params=_params, @@ -6859,12 +12194,16 @@ async def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: except (StreamConsumedError, StreamClosedError): pass map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.Schedule, response.json()) + deserialized = _deserialize(_models.ToolboxObject, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -6875,25 +12214,35 @@ async def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: def list( self, *, - type: Optional[Union[str, _models.ScheduleTaskType]] = None, - enabled: Optional[bool] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, **kwargs: Any - ) -> AsyncItemPaged["_models.Schedule"]: - """List all schedules. + ) -> AsyncItemPaged["_models.ToolboxObject"]: + """List all toolboxes. - :keyword type: Filter by the type of schedule. Known values are: "Evaluation" and "Insight". + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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 type: str or ~azure.ai.projects.models.ScheduleTaskType - :keyword enabled: Filter by the enabled status. Default value is None. - :paramtype enabled: bool - :return: An iterator like instance of Schedule - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.Schedule] + :paramtype before: str + :return: An iterator like instance of ToolboxObject + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.ToolboxObject] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.Schedule]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.ToolboxObject]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -6903,60 +12252,129 @@ def list( } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(next_link=None): - if not next_link: + def prepare_request(_continuation_token=None): - _request = build_beta_schedules_list_request( - type=type, - enabled=enabled, - api_version=self._config.api_version, - headers=_headers, - params=_params, + _request = build_beta_toolboxes_list_request( + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.ToolboxObject], + 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) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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, ) - 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) + raise HttpResponseError(response=response, model=error) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace + def list_versions( + self, + name: str, + *, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.ToolboxVersionObject"]: + """List all versions of a toolbox. + + :param name: The name of the toolbox to list versions for. Required. + :type name: str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of ToolboxVersionObject + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.ToolboxVersionObject] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.ToolboxVersionObject]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", - urllib.parse.urljoin(next_link, _parsed_next_link.path), - params=_next_request_params, - headers=_headers, - ) - 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) + def prepare_request(_continuation_token=None): + _request = build_beta_toolboxes_list_versions_request( + name=name, + 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 async def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.Schedule], - deserialized.get("value", []), + List[_models.ToolboxVersionObject], + deserialized.get("data", []), ) if cls: list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + return deserialized.get("last_id") or None, AsyncList(list_of_elem) - async def get_next(next_link=None): - _request = prepare_request(next_link) + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) _stream = False pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access @@ -6966,79 +12384,26 @@ async def get_next(next_link=None): if response.status_code not in [200]: map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) return pipeline_response return AsyncItemPaged(get_next, extract_data) - @overload - 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. - - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :param schedule: The resource instance. Required. - :type schedule: ~azure.ai.projects.models.Schedule - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: Schedule. The Schedule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Schedule - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - 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. - - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :param schedule: The resource instance. Required. - :type schedule: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: Schedule. The Schedule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Schedule - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - 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. - - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :param schedule: The resource instance. Required. - :type schedule: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: Schedule. The Schedule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Schedule - :raises ~azure.core.exceptions.HttpResponseError: - """ - @distributed_trace_async - 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. + async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.ToolboxVersionObject: + """Retrieve a specific version of a toolbox. - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :param schedule: The resource instance. Is one of the following types: Schedule, JSON, - IO[bytes] Required. - :type schedule: ~azure.ai.projects.models.Schedule or JSON or IO[bytes] - :return: Schedule. The Schedule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Schedule + :param name: The name of the toolbox. Required. + :type name: str + :param version: The version identifier to retrieve. Required. + :type version: str + :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxVersionObject :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7049,24 +12414,15 @@ async def create_or_update( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.Schedule] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _content = None - if isinstance(schedule, (IOBase, bytes)): - _content = schedule - else: - _content = json.dumps(schedule, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[_models.ToolboxVersionObject] = kwargs.pop("cls", None) - _request = build_beta_schedules_create_or_update_request( - schedule_id=schedule_id, - content_type=content_type, + _request = build_beta_toolboxes_get_version_request( + name=name, + version=version, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -7083,35 +12439,99 @@ async def create_or_update( response = pipeline_response.http_response - if response.status_code not in [200, 201]: + 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) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.Schedule, response.json()) + deserialized = _deserialize(_models.ToolboxVersionObject, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + @overload + async def update( + self, name: str, *, default_version: str, content_type: str = "application/json", **kwargs: Any + ) -> _models.ToolboxObject: + """Update a toolbox to point to a specific 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, + the toolbox's default version will resolve to this version instead of the latest. Required. + :paramtype default_version: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update( + self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.ToolboxObject: + """Update a toolbox to point to a specific version. + + :param name: The name of the toolbox to update. Required. + :type name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update( + self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.ToolboxObject: + """Update a toolbox to point to a specific version. + + :param name: The name of the toolbox to update. Required. + :type name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + @distributed_trace_async - async def get_run(self, schedule_id: str, run_id: str, **kwargs: Any) -> _models.ScheduleRun: - """Get a schedule run by id. + async def update( + self, name: str, body: Union[JSON, IO[bytes]] = _Unset, *, default_version: str = _Unset, **kwargs: Any + ) -> _models.ToolboxObject: + """Update a toolbox to point to a specific version. - :param schedule_id: The unique identifier of the schedule. Required. - :type schedule_id: str - :param run_id: The unique identifier of the schedule run. Required. - :type run_id: str - :return: ScheduleRun. The ScheduleRun is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ScheduleRun + :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. + :type body: JSON or IO[bytes] + :keyword default_version: The version identifier that the toolbox should point to. When set, + the toolbox's default version will resolve to this version instead of the latest. Required. + :paramtype default_version: str + :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxObject :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7122,15 +12542,29 @@ async def get_run(self, schedule_id: str, run_id: str, **kwargs: Any) -> _models } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.ScheduleRun] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.ToolboxObject] = kwargs.pop("cls", None) - _request = build_beta_schedules_get_run_request( - schedule_id=schedule_id, - run_id=run_id, + if body is _Unset: + if default_version is _Unset: + raise TypeError("missing required argument: default_version") + body = {"default_version": default_version} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_toolboxes_update_request( + name=name, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -7163,40 +12597,23 @@ async def get_run(self, schedule_id: str, run_id: str, **kwargs: Any) -> _models if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.ScheduleRun, response.json()) + deserialized = _deserialize(_models.ToolboxObject, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @distributed_trace - def list_runs( - self, - schedule_id: str, - *, - type: Optional[Union[str, _models.ScheduleTaskType]] = None, - enabled: Optional[bool] = None, - **kwargs: Any - ) -> AsyncItemPaged["_models.ScheduleRun"]: - """List all schedule runs. + @distributed_trace_async + async def delete(self, name: str, **kwargs: Any) -> None: + """Delete a toolbox and all its versions. - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :keyword type: Filter by the type of schedule. Known values are: "Evaluation" and "Insight". - Default value is None. - :paramtype type: str or ~azure.ai.projects.models.ScheduleTaskType - :keyword enabled: Filter by the enabled status. Default value is None. - :paramtype enabled: bool - :return: An iterator like instance of ScheduleRun - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.ScheduleRun] + :param name: The name of the toolbox to delete. Required. + :type name: str + :return: None + :rtype: None :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[List[_models.ScheduleRun]] = kwargs.pop("cls", None) - error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -7205,196 +12622,50 @@ def list_runs( } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(next_link=None): - if not next_link: - - _request = build_beta_schedules_list_runs_request( - schedule_id=schedule_id, - type=type, - enabled=enabled, - 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) + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", - urllib.parse.urljoin(next_link, _parsed_next_link.path), - params=_next_request_params, - headers=_headers, - ) - 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) + cls: ClsType[None] = kwargs.pop("cls", None) - return _request + _request = build_beta_toolboxes_delete_request( + name=name, + 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) - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.ScheduleRun], - deserialized.get("value", []), - ) - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) - async def get_next(next_link=None): - _request = prepare_request(next_link) + response = pipeline_response.http_response - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return AsyncItemPaged(get_next, extract_data) - - -class BetaToolboxesOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.aio.AIProjectClient`'s - :attr:`toolboxes` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - - @overload - async def create_version( - self, - name: str, - *, - tools: List[_models.Tool], - content_type: str = "application/json", - description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - 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. - - :param name: The name of the toolbox. If the toolbox does not exist, it will be created. - Required. - :type name: str - :keyword tools: The list of tools to include in this version. Required. - :paramtype tools: list[~azure.ai.projects.models.Tool] - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :keyword description: A human-readable description of the toolbox. Default value is None. - :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the toolbox. Default value is - None. - :paramtype metadata: dict[str, str] - :keyword policies: Policy configuration for this toolbox version. Default value is None. - :paramtype policies: ~azure.ai.projects.models.ToolboxPolicies - :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxVersionObject - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - 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. - - :param name: The name of the toolbox. If the toolbox does not exist, it will be created. - Required. - :type name: str - :param body: Required. - :type body: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxVersionObject - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - 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. + raise HttpResponseError(response=response, model=error) - :param name: The name of the toolbox. If the toolbox does not exist, it will be created. - Required. - :type name: str - :param body: Required. - :type body: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxVersionObject - :raises ~azure.core.exceptions.HttpResponseError: - """ + if cls: + return cls(pipeline_response, None, {}) # type: ignore @distributed_trace_async - async def create_version( - self, - name: str, - body: Union[JSON, IO[bytes]] = _Unset, - *, - tools: List[_models.Tool] = _Unset, - description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - 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. + async def delete_version(self, name: str, version: str, **kwargs: Any) -> None: + """Delete a specific version of a toolbox. - :param name: The name of the toolbox. If the toolbox does not exist, it will be created. - Required. + :param name: The name of the toolbox. Required. :type name: str - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword tools: The list of tools to include in this version. Required. - :paramtype tools: list[~azure.ai.projects.models.Tool] - :keyword description: A human-readable description of the toolbox. Default value is None. - :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the toolbox. Default value is - None. - :paramtype metadata: dict[str, str] - :keyword policies: Policy configuration for this toolbox version. Default value is None. - :paramtype policies: ~azure.ai.projects.models.ToolboxPolicies - :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxVersionObject + :param version: The version identifier to delete. Required. + :type version: str + :return: None + :rtype: None :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7405,29 +12676,15 @@ async def create_version( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.ToolboxVersionObject] = kwargs.pop("cls", None) + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - if body is _Unset: - if tools is _Unset: - raise TypeError("missing required argument: tools") - body = {"description": description, "metadata": metadata, "policies": policies, "tools": tools} - body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[None] = kwargs.pop("cls", None) - _request = build_beta_toolboxes_create_version_request( + _request = build_beta_toolboxes_delete_version_request( name=name, - content_type=content_type, + version=version, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -7436,20 +12693,14 @@ async def create_version( } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = False pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - 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 + if response.status_code not in [204]: map_error(status_code=response.status_code, response=response, error_map=error_map) error = _failsafe_deserialize( _models.ApiErrorResponse, @@ -7457,24 +12708,35 @@ async def create_version( ) raise HttpResponseError(response=response, model=error) - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.ToolboxVersionObject, response.json()) - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, None, {}) # type: ignore - return deserialized # type: ignore + +class BetaSkillsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.aio.AIProjectClient`'s + :attr:`skills` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") @distributed_trace_async - async def get(self, name: str, **kwargs: Any) -> _models.ToolboxObject: - """Retrieve a toolbox. + async def get(self, name: str, **kwargs: Any) -> _models.SkillDetails: + """Retrieves a skill. - :param name: The name of the toolbox to retrieve. Required. + :param name: The unique name of the skill. Required. :type name: str - :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxObject + :return: SkillDetails. The SkillDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillDetails :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7488,9 +12750,9 @@ async def get(self, name: str, **kwargs: Any) -> _models.ToolboxObject: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.ToolboxObject] = kwargs.pop("cls", None) + cls: ClsType[_models.SkillDetails] = kwargs.pop("cls", None) - _request = build_beta_toolboxes_get_request( + _request = build_beta_skills_get_request( name=name, api_version=self._config.api_version, headers=_headers, @@ -7525,7 +12787,7 @@ async def get(self, name: str, **kwargs: Any) -> _models.ToolboxObject: if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.ToolboxObject, response.json()) + deserialized = _deserialize(_models.SkillDetails, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -7540,8 +12802,8 @@ def list( order: Optional[Union[str, _models.PageOrder]] = None, before: Optional[str] = None, **kwargs: Any - ) -> AsyncItemPaged["_models.ToolboxObject"]: - """List all toolboxes. + ) -> AsyncItemPaged["_models.SkillDetails"]: + """Returns the list of all skills. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -7557,14 +12819,14 @@ def list( 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: An iterator like instance of ToolboxObject - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.ToolboxObject] + :return: An iterator like instance of SkillDetails + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.SkillDetails] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.ToolboxObject]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.SkillDetails]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -7576,7 +12838,7 @@ def list( def prepare_request(_continuation_token=None): - _request = build_beta_toolboxes_list_request( + _request = build_beta_skills_list_request( limit=limit, order=order, after=_continuation_token, @@ -7594,7 +12856,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.ToolboxObject], + List[_models.SkillDetails], deserialized.get("data", []), ) if cls: @@ -7622,43 +12884,78 @@ async def get_next(_continuation_token=None): return AsyncItemPaged(get_next, extract_data) - @distributed_trace - def list_versions( - self, - name: str, - *, - limit: Optional[int] = None, - order: Optional[Union[str, _models.PageOrder]] = None, - before: Optional[str] = None, - **kwargs: Any - ) -> AsyncItemPaged["_models.ToolboxVersionObject"]: - """List all versions of a toolbox. + @overload + async def update( + self, name: str, *, default_version: str, content_type: str = "application/json", **kwargs: Any + ) -> _models.SkillDetails: + """Update a skill. - :param name: The name of the toolbox to list versions for. Required. + :param name: The name of the skill to update. Required. :type name: str - :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the - default is 20. Default value is None. - :paramtype limit: int - :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for - 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 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: An iterator like instance of ToolboxVersionObject - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.ToolboxVersionObject] + :keyword default_version: The version identifier that the skill should point to. When set, the + skill's default version will resolve to this version instead of the latest. Required. + :paramtype default_version: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: SkillDetails. The SkillDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillDetails :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.ToolboxVersionObject]] = kwargs.pop("cls", None) + @overload + async def update( + self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.SkillDetails: + """Update a skill. + + :param name: The name of the skill to update. Required. + :type name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: SkillDetails. The SkillDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update( + self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.SkillDetails: + """Update a skill. + + :param name: The name of the skill to update. Required. + :type name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: SkillDetails. The SkillDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + @distributed_trace_async + async def update( + self, name: str, body: Union[JSON, IO[bytes]] = _Unset, *, default_version: str = _Unset, **kwargs: Any + ) -> _models.SkillDetails: + """Update a skill. + + :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. + :type body: JSON or IO[bytes] + :keyword default_version: The version identifier that the skill should point to. When set, the + skill's default version will resolve to this version instead of the latest. Required. + :paramtype default_version: str + :return: SkillDetails. The SkillDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -7667,65 +12964,76 @@ def list_versions( } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(_continuation_token=None): + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} - _request = build_beta_toolboxes_list_versions_request( - name=name, - 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 + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.SkillDetails] = kwargs.pop("cls", None) - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.ToolboxVersionObject], - 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) + if body is _Unset: + if default_version is _Unset: + raise TypeError("missing required argument: default_version") + body = {"default_version": default_version} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - async def get_next(_continuation_token=None): - _request = prepare_request(_continuation_token) + _request = build_beta_skills_update_request( + name=name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs + _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 + ) + + response = pipeline_response.http_response + + 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, ) - response = pipeline_response.http_response + raise HttpResponseError(response=response, model=error) - 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 _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.SkillDetails, response.json()) - return pipeline_response + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore - return AsyncItemPaged(get_next, extract_data) + return deserialized # type: ignore @distributed_trace_async - async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.ToolboxVersionObject: - """Retrieve a specific version of a toolbox. + async def delete(self, name: str, **kwargs: Any) -> _models.DeleteSkillResult: + """Deletes a skill. - :param name: The name of the toolbox. Required. + :param name: The unique name of the skill. Required. :type name: str - :param version: The version identifier to retrieve. Required. - :type version: str - :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxVersionObject + :return: DeleteSkillResult. The DeleteSkillResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DeleteSkillResult :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7739,11 +13047,10 @@ async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.T _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.ToolboxVersionObject] = kwargs.pop("cls", None) + cls: ClsType[_models.DeleteSkillResult] = kwargs.pop("cls", None) - _request = build_beta_toolboxes_get_version_request( + _request = build_beta_skills_delete_request( name=name, - version=version, api_version=self._config.api_version, headers=_headers, params=_params, @@ -7777,7 +13084,7 @@ async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.T if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.ToolboxVersionObject, response.json()) + deserialized = _deserialize(_models.DeleteSkillResult, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -7785,75 +13092,91 @@ async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.T return deserialized # type: ignore @overload - async def update( - self, name: str, *, default_version: str, content_type: str = "application/json", **kwargs: Any - ) -> _models.ToolboxObject: - """Update a toolbox to point to a specific version. + async def create( + self, + name: str, + *, + content_type: str = "application/json", + inline_content: Optional[_models.SkillInlineContent] = None, + 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. - :param name: The name of the toolbox to update. Required. + :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str - :keyword default_version: The version identifier that the toolbox should point to. When set, - the toolbox's default version will resolve to this version instead of the latest. Required. - :paramtype default_version: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxObject + :keyword inline_content: Inline skill content for simple skills without file uploads. + Foundry-specific extension. Default value is None. + :paramtype inline_content: ~azure.ai.projects.models.SkillInlineContent + :keyword default: Whether to set this version as the default. Default value is None. + :paramtype default: bool + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ @overload - async def update( + async def create( self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.ToolboxObject: - """Update a toolbox to point to a specific version. + ) -> _models.SkillVersion: + """Creates a new version of a skill. If the skill does not exist, it will be created. - :param name: The name of the toolbox to update. Required. + :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str :param body: Required. :type body: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxObject + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ @overload - async def update( + async def create( self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.ToolboxObject: - """Update a toolbox to point to a specific version. + ) -> _models.SkillVersion: + """Creates a new version of a skill. If the skill does not exist, it will be created. - :param name: The name of the toolbox to update. Required. + :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str :param body: Required. :type body: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxObject + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace_async - async def update( - self, name: str, body: Union[JSON, IO[bytes]] = _Unset, *, default_version: str = _Unset, **kwargs: Any - ) -> _models.ToolboxObject: - """Update a toolbox to point to a specific version. + async def create( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + inline_content: Optional[_models.SkillInlineContent] = None, + 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. - :param name: The name of the toolbox to update. Required. + :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] - :keyword default_version: The version identifier that the toolbox should point to. When set, - the toolbox's default version will resolve to this version instead of the latest. Required. - :paramtype default_version: str - :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxObject + :keyword inline_content: Inline skill content for simple skills without file uploads. + Foundry-specific extension. Default value is None. + :paramtype inline_content: ~azure.ai.projects.models.SkillInlineContent + :keyword default: Whether to set this version as the default. Default value is None. + :paramtype default: bool + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7868,12 +13191,10 @@ async def update( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.ToolboxObject] = kwargs.pop("cls", None) + cls: ClsType[_models.SkillVersion] = kwargs.pop("cls", None) if body is _Unset: - if default_version is _Unset: - raise TypeError("missing required argument: default_version") - body = {"default_version": default_version} + body = {"default": default, "inline_content": inline_content} body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None @@ -7882,7 +13203,7 @@ async def update( else: _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_toolboxes_update_request( + _request = build_beta_skills_create_request( name=name, content_type=content_type, api_version=self._config.api_version, @@ -7919,21 +13240,53 @@ async def update( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.ToolboxObject, response.json()) + deserialized = _deserialize(_models.SkillVersion, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + @overload + 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. + + :param name: The name of the skill. Required. + :type name: str + :param content: Required. + :type content: ~azure.ai.projects.models.CreateSkillVersionFromFilesBody + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @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. + + :param name: The name of the skill. Required. + :type name: str + :param content: Required. + :type content: JSON + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + @distributed_trace_async - async def delete(self, name: str, **kwargs: Any) -> None: - """Delete a toolbox and all its versions. + 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. - :param name: The name of the toolbox to delete. Required. + :param name: The name of the skill. Required. :type name: str - :return: None - :rtype: None + :param content: Is either a CreateSkillVersionFromFilesBody type or a JSON type. Required. + :type content: ~azure.ai.projects.models.CreateSkillVersionFromFilesBody or JSON + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7947,11 +13300,17 @@ async def delete(self, name: str, **kwargs: Any) -> None: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[None] = kwargs.pop("cls", None) + cls: ClsType[_models.SkillVersion] = kwargs.pop("cls", None) - _request = build_beta_toolboxes_delete_request( + _body = content.as_dict() if isinstance(content, _Model) else content + _file_fields: list[str] = ["files"] + _data_fields: list[str] = ["default"] + _files = prepare_multipart_form_data(_body, _file_fields, _data_fields) + + _request = build_beta_skills_create_from_files_request( name=name, api_version=self._config.api_version, + files=_files, headers=_headers, params=_params, ) @@ -7960,14 +13319,20 @@ async def delete(self, name: str, **kwargs: Any) -> None: } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _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 ) response = pipeline_response.http_response - if response.status_code not in [204]: + 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, @@ -7975,19 +13340,120 @@ async def delete(self, name: str, **kwargs: Any) -> None: ) raise HttpResponseError(response=response, model=error) + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.SkillVersion, response.json()) + if cls: - return cls(pipeline_response, None, {}) # type: ignore + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_versions( + self, + name: str, + *, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.SkillVersion"]: + """List all versions of a skill. + + :param name: The name of the skill to list versions for. Required. + :type name: str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of SkillVersion + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.SkillVersion] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.SkillVersion]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_beta_skills_list_versions_request( + name=name, + 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 + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.SkillVersion], + 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) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) @distributed_trace_async - async def delete_version(self, name: str, version: str, **kwargs: Any) -> None: - """Delete a specific version of a toolbox. + async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.SkillVersion: + """Retrieve a specific version of a skill. - :param name: The name of the toolbox. Required. + :param name: The name of the skill. Required. :type name: str - :param version: The version identifier to delete. Required. + :param version: The version identifier to retrieve. Required. :type version: str - :return: None - :rtype: None + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8001,9 +13467,9 @@ async def delete_version(self, name: str, version: str, **kwargs: Any) -> None: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[None] = kwargs.pop("cls", None) + cls: ClsType[_models.SkillVersion] = kwargs.pop("cls", None) - _request = build_beta_toolboxes_delete_version_request( + _request = build_beta_skills_get_version_request( name=name, version=version, api_version=self._config.api_version, @@ -8015,14 +13481,20 @@ async def delete_version(self, name: str, version: str, **kwargs: Any) -> None: } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _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 ) response = pipeline_response.http_response - if response.status_code not in [204]: + 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, @@ -8030,123 +13502,24 @@ async def delete_version(self, name: str, version: str, **kwargs: Any) -> None: ) raise HttpResponseError(response=response, model=error) - if cls: - return cls(pipeline_response, None, {}) # type: ignore - - -class BetaSkillsOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.aio.AIProjectClient`'s - :attr:`skills` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - - @overload - async def create( - self, - *, - name: str, - content_type: str = "application/json", - description: Optional[str] = None, - instructions: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - **kwargs: Any - ) -> _models.SkillObject: - """Creates a skill. - - :keyword name: The unique name of the skill. Required. - :paramtype name: str - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :keyword description: A human-readable description of the skill. Default value is None. - :paramtype description: str - :keyword instructions: Instructions that define the behavior of the skill. Default value is - None. - :paramtype instructions: str - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - async def create(self, body: JSON, *, content_type: str = "application/json", **kwargs: Any) -> _models.SkillObject: - """Creates a skill. - - :param body: Required. - :type body: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - async def create( - self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.SkillObject: - """Creates a skill. + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.SkillVersion, response.json()) - :param body: Required. - :type body: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject - :raises ~azure.core.exceptions.HttpResponseError: - """ + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore - @distributed_trace_async - async def create( - self, - body: Union[JSON, IO[bytes]] = _Unset, - *, - name: str = _Unset, - description: Optional[str] = None, - instructions: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - **kwargs: Any - ) -> _models.SkillObject: - """Creates a skill. + return deserialized # type: ignore - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword name: The unique name of the skill. Required. - :paramtype name: str - :keyword description: A human-readable description of the skill. Default value is None. - :paramtype description: str - :keyword instructions: Instructions that define the behavior of the skill. Default value is - None. - :paramtype instructions: str - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. + @distributed_trace_async + async def download(self, name: str, **kwargs: Any) -> AsyncIterator[bytes]: + """Download the zip content for the default version of a skill. - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :param name: The name of the skill. Required. + :type name: str + :return: AsyncIterator[bytes] + :rtype: AsyncIterator[bytes] :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8157,28 +13530,14 @@ async def create( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.SkillObject] = kwargs.pop("cls", None) - - if body is _Unset: - if name is _Unset: - raise TypeError("missing required argument: name") - body = {"description": description, "instructions": instructions, "metadata": metadata, "name": name} - body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None) - _request = build_beta_skills_create_request( - content_type=content_type, + _request = build_beta_skills_download_request( + name=name, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -8188,14 +13547,14 @@ async def create( _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = kwargs.pop("stream", True) pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - if response.status_code not in [201]: + if response.status_code not in [200]: if _stream: try: await response.read() # Load the body in memory and close the socket @@ -8208,24 +13567,26 @@ async def create( ) raise HttpResponseError(response=response, model=error) - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.SkillObject, response.json()) + response_headers = {} + response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, deserialized, response_headers) # type: ignore return deserialized # type: ignore @distributed_trace_async - async def create_from_package(self, body: bytes, **kwargs: Any) -> _models.SkillObject: - """Creates a skill from a zip package. + async def download_version(self, name: str, version: str, **kwargs: Any) -> AsyncIterator[bytes]: + """Download the zip content for a specific version of a skill. - :param body: The zip package used to create the skill. Required. - :type body: bytes - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :param name: The name of the skill. Required. + :type name: str + :param version: The version to download content for. Required. + :type version: str + :return: AsyncIterator[bytes] + :rtype: AsyncIterator[bytes] :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8236,18 +13597,15 @@ async def create_from_package(self, body: bytes, **kwargs: Any) -> _models.Skill } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: str = kwargs.pop("content_type", _headers.pop("Content-Type", "application/zip")) - cls: ClsType[_models.SkillObject] = kwargs.pop("cls", None) - - _content = body + cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None) - _request = build_beta_skills_create_from_package_request( - content_type=content_type, + _request = build_beta_skills_download_version_request( + name=name, + version=version, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -8257,14 +13615,14 @@ async def create_from_package(self, body: bytes, **kwargs: Any) -> _models.Skill _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = kwargs.pop("stream", True) pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - if response.status_code not in [201]: + if response.status_code not in [200]: if _stream: try: await response.read() # Load the body in memory and close the socket @@ -8277,24 +13635,27 @@ async def create_from_package(self, body: bytes, **kwargs: Any) -> _models.Skill ) raise HttpResponseError(response=response, model=error) - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.SkillObject, response.json()) + response_headers = {} + response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, deserialized, response_headers) # type: ignore return deserialized # type: ignore @distributed_trace_async - async def get(self, name: str, **kwargs: Any) -> _models.SkillObject: - """Retrieves a skill. + async def delete_version(self, name: str, version: str, **kwargs: Any) -> _models.DeleteSkillVersionResponse: + """Delete a specific version of a skill. - :param name: The unique name of the skill. Required. + :param name: The name of the skill. Required. :type name: str - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :param version: The version identifier to delete. Required. + :type version: str + :return: DeleteSkillVersionResponse. The DeleteSkillVersionResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.DeleteSkillVersionResponse :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8308,10 +13669,11 @@ async def get(self, name: str, **kwargs: Any) -> _models.SkillObject: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.SkillObject] = kwargs.pop("cls", None) + cls: ClsType[_models.DeleteSkillVersionResponse] = kwargs.pop("cls", None) - _request = build_beta_skills_get_request( + _request = build_beta_skills_delete_version_request( name=name, + version=version, api_version=self._config.api_version, headers=_headers, params=_params, @@ -8345,21 +13707,41 @@ async def get(self, name: str, **kwargs: Any) -> _models.SkillObject: if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.SkillObject, response.json()) + deserialized = _deserialize(_models.DeleteSkillVersionResponse, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + +class BetaDatasetsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.aio.AIProjectClient`'s + :attr:`datasets` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + @distributed_trace_async - async def download(self, name: str, **kwargs: Any) -> AsyncIterator[bytes]: - """Downloads a skill package. + async def get_generation_job(self, job_id: str, **kwargs: Any) -> _models.DataGenerationJob: + """Get info about a data generation job. - :param name: The unique name of the skill. Required. - :type name: str - :return: AsyncIterator[bytes] - :rtype: AsyncIterator[bytes] + Gets the details of a data generation job by its ID. + + :param job_id: The ID of the job. Required. + :type job_id: str + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8373,10 +13755,10 @@ async def download(self, name: str, **kwargs: Any) -> AsyncIterator[bytes]: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None) + cls: ClsType[_models.DataGenerationJob] = kwargs.pop("cls", None) - _request = build_beta_skills_download_request( - name=name, + _request = build_beta_datasets_get_generation_job_request( + job_id=job_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -8387,7 +13769,7 @@ async def download(self, name: str, **kwargs: Any) -> AsyncIterator[bytes]: _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", True) + _stream = kwargs.pop("stream", False) pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) @@ -8408,9 +13790,12 @@ async def download(self, name: str, **kwargs: Any) -> AsyncIterator[bytes]: raise HttpResponseError(response=response, model=error) response_headers = {} - response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + response_headers["Retry-After"] = self._deserialize("int", response.headers.get("Retry-After")) - deserialized = response.iter_bytes() if _decompress else response.iter_raw() + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DataGenerationJob, response.json()) if cls: return cls(pipeline_response, deserialized, response_headers) # type: ignore @@ -8418,15 +13803,17 @@ async def download(self, name: str, **kwargs: Any) -> AsyncIterator[bytes]: return deserialized # type: ignore @distributed_trace - def list( + def list_generation_jobs( self, *, limit: Optional[int] = None, order: Optional[Union[str, _models.PageOrder]] = None, before: Optional[str] = None, **kwargs: Any - ) -> AsyncItemPaged["_models.SkillObject"]: - """Returns the list of all skills. + ) -> AsyncItemPaged["_models.DataGenerationJob"]: + """Returns a list of data generation jobs. + + Returns a list of data generation jobs. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -8442,14 +13829,14 @@ def list( 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: An iterator like instance of SkillObject - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.SkillObject] + :return: An iterator like instance of DataGenerationJob + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.DataGenerationJob] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.SkillObject]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.DataGenerationJob]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -8461,7 +13848,7 @@ def list( def prepare_request(_continuation_token=None): - _request = build_beta_skills_list_request( + _request = build_beta_datasets_list_generation_jobs_request( limit=limit, order=order, after=_continuation_token, @@ -8479,7 +13866,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.SkillObject], + List[_models.DataGenerationJob], deserialized.get("data", []), ) if cls: @@ -8508,107 +13895,98 @@ async def get_next(_continuation_token=None): return AsyncItemPaged(get_next, extract_data) @overload - async def update( + async def create_generation_job( self, - name: str, + job: _models.DataGenerationJob, *, + operation_id: Optional[str] = None, content_type: str = "application/json", - description: Optional[str] = None, - instructions: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, **kwargs: Any - ) -> _models.SkillObject: - """Updates an existing skill. + ) -> _models.DataGenerationJob: + """Creates a data generation job. - :param name: The unique name of the skill. Required. - :type name: str + Creates a data generation job. + + :param job: The job to create. Required. + :type job: ~azure.ai.projects.models.DataGenerationJob + :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 :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :keyword description: A human-readable description of the skill. Default value is None. - :paramtype description: str - :keyword instructions: Instructions that define the behavior of the skill. Default value is - None. - :paramtype instructions: str - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ @overload - async def update( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.SkillObject: - """Updates an existing skill. - - :param name: The unique name of the skill. Required. - :type name: str - :param body: Required. - :type body: JSON + 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. + + Creates a data generation job. + + :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 :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ @overload - async def update( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.SkillObject: - """Updates an existing skill. + async def create_generation_job( + self, + job: IO[bytes], + *, + operation_id: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.DataGenerationJob: + """Creates a data generation job. - :param name: The unique name of the skill. Required. - :type name: str - :param body: Required. - :type body: IO[bytes] + Creates a data generation job. + + :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 :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace_async - async def update( + async def create_generation_job( self, - name: str, - body: Union[JSON, IO[bytes]] = _Unset, + job: Union[_models.DataGenerationJob, JSON, IO[bytes]], *, - description: Optional[str] = None, - instructions: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, + operation_id: Optional[str] = None, **kwargs: Any - ) -> _models.SkillObject: - """Updates an existing skill. + ) -> _models.DataGenerationJob: + """Creates a data generation job. - :param name: The unique name of the skill. Required. - :type name: str - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword description: A human-readable description of the skill. Default value is None. - :paramtype description: str - :keyword instructions: Instructions that define the behavior of the skill. Default value is - None. - :paramtype instructions: str - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. + Creates a data generation job. - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :param job: The job to create. Is one of the following types: DataGenerationJob, JSON, + IO[bytes] Required. + :type job: ~azure.ai.projects.models.DataGenerationJob 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 + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8623,20 +14001,17 @@ async def update( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.SkillObject] = kwargs.pop("cls", None) + cls: ClsType[_models.DataGenerationJob] = kwargs.pop("cls", None) - if body is _Unset: - body = {"description": description, "instructions": instructions, "metadata": metadata} - body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None - if isinstance(body, (IOBase, bytes)): - _content = body + if isinstance(job, (IOBase, bytes)): + _content = job else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(job, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_skills_update_request( - name=name, + _request = build_beta_datasets_create_generation_job_request( + operation_id=operation_id, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -8656,7 +14031,7 @@ async def update( response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [201]: if _stream: try: await response.read() # Load the body in memory and close the socket @@ -8669,24 +14044,30 @@ async def update( ) raise HttpResponseError(response=response, model=error) + response_headers = {} + response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) + response_headers["Location"] = self._deserialize("str", response.headers.get("Location")) + if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.SkillObject, response.json()) + deserialized = _deserialize(_models.DataGenerationJob, response.json()) if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, deserialized, response_headers) # type: ignore return deserialized # type: ignore @distributed_trace_async - async def delete(self, name: str, **kwargs: Any) -> _models.DeleteSkillResponse: - """Deletes a skill. + async def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.DataGenerationJob: + """Cancels a data generation job. - :param name: The unique name of the skill. Required. - :type name: str - :return: DeleteSkillResponse. The DeleteSkillResponse is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.DeleteSkillResponse + Cancels a data generation job by its ID. + + :param job_id: The ID of the job to cancel. Required. + :type job_id: str + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8700,10 +14081,10 @@ async def delete(self, name: str, **kwargs: Any) -> _models.DeleteSkillResponse: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.DeleteSkillResponse] = kwargs.pop("cls", None) + cls: ClsType[_models.DataGenerationJob] = kwargs.pop("cls", None) - _request = build_beta_skills_delete_request( - name=name, + _request = build_beta_datasets_cancel_generation_job_request( + job_id=job_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -8737,9 +14118,63 @@ async def delete(self, name: str, **kwargs: Any) -> _models.DeleteSkillResponse: if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.DeleteSkillResponse, response.json()) + deserialized = _deserialize(_models.DataGenerationJob, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + + @distributed_trace_async + async def delete_generation_job(self, job_id: str, **kwargs: Any) -> None: + """Deletes a data generation job. + + Deletes a data generation job by its ID. + + :param job_id: The ID of the job to delete. Required. + :type job_id: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_datasets_delete_generation_job_request( + job_id=job_id, + 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) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + 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, None, {}) # type: ignore diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py index 56dea6851b0d..9840dd5a6ac6 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py @@ -15,14 +15,17 @@ from ._patch_telemetry_async import TelemetryOperations from ._patch_connections_async import ConnectionsOperations from ._patch_memories_async import BetaMemoryStoresOperations +from ._patch_models_async import BetaModelsOperations from ._patch_sessions_async import BetaAgentsOperations from ...operations._patch import _BETA_OPERATION_FEATURE_HEADERS, _OperationMethodHeaderProxy from ._operations import ( + BetaDatasetsOperations, BetaEvaluationTaxonomiesOperations, BetaEvaluatorsOperations, BetaInsightsOperations, BetaOperations as GeneratedBetaOperations, BetaRedTeamsOperations, + BetaRoutinesOperations, BetaSchedulesOperations, BetaSkillsOperations, BetaToolboxesOperations, @@ -49,14 +52,20 @@ class BetaOperations(GeneratedBetaOperations): """:class:`~azure.ai.projects.aio.operations.BetaInsightsOperations` operations""" memory_stores: BetaMemoryStoresOperations """:class:`~azure.ai.projects.aio.operations.BetaMemoryStoresOperations` operations""" + models: BetaModelsOperations + """:class:`~azure.ai.projects.aio.operations.BetaModelsOperations` operations""" red_teams: BetaRedTeamsOperations """:class:`~azure.ai.projects.aio.operations.BetaRedTeamsOperations` operations""" + routines: BetaRoutinesOperations + """:class:`~azure.ai.projects.aio.operations.BetaRoutinesOperations` operations""" schedules: BetaSchedulesOperations """:class:`~azure.ai.projects.aio.operations.BetaSchedulesOperations` operations""" toolboxes: BetaToolboxesOperations """:class:`~azure.ai.projects.aio.operations.BetaToolboxesOperations` operations""" skills: BetaSkillsOperations """:class:`~azure.ai.projects.aio.operations.BetaSkillsOperations` operations""" + datasets: BetaDatasetsOperations + """:class:`~azure.ai.projects.aio.operations.BetaDatasetsOperations` operations""" def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -66,6 +75,8 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.agents = BetaAgentsOperations(self._client, self._config, self._serialize, self._deserialize) # Replace with patched class that includes begin_update_memories self.memory_stores = BetaMemoryStoresOperations(self._client, self._config, self._serialize, self._deserialize) + # Replace with patched class that includes create (3-step upload helper) + self.models = BetaModelsOperations(self._client, self._config, self._serialize, self._deserialize) for property_name, foundry_features_value in _BETA_OPERATION_FEATURE_HEADERS.items(): setattr( @@ -78,12 +89,15 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: __all__: List[str] = [ "AgentsOperations", "BetaAgentsOperations", + "BetaDatasetsOperations", "BetaEvaluationTaxonomiesOperations", "BetaEvaluatorsOperations", "BetaInsightsOperations", "BetaMemoryStoresOperations", + "BetaModelsOperations", "BetaOperations", "BetaRedTeamsOperations", + "BetaRoutinesOperations", "BetaSchedulesOperations", "BetaSkillsOperations", "BetaToolboxesOperations", diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_agents_async.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_agents_async.py index f2a6dbc62624..46a4d7a9e456 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_agents_async.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_agents_async.py @@ -13,8 +13,9 @@ from azure.core.tracing.decorator_async import distributed_trace_async from ._operations import AgentsOperations as GeneratedAgentsOperations, JSON, _Unset from ... import models as _models -from ...models._patch import _FOUNDRY_FEATURES_HEADER_NAME, _has_header_case_insensitive -from ...operations._patch_agents import ( +from ...models._patch import ( + _FOUNDRY_FEATURES_HEADER_NAME, + _has_header_case_insensitive, _AGENT_OPERATION_FEATURE_HEADERS, _PREVIEW_FEATURE_REQUIRED_CODE, _PREVIEW_FEATURE_ADDED_ERROR_MESSAGE, @@ -40,6 +41,7 @@ async def create_version( content_type: str = "application/json", metadata: Optional[dict[str, str]] = None, description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, **kwargs: Any, ) -> _models.AgentVersionDetails: """Create a new agent version. @@ -66,6 +68,8 @@ async def create_version( :paramtype metadata: dict[str, str] :keyword description: A human-readable description of the agent. Default value is None. :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.projects.models.AgentBlueprintReference :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping :rtype: ~azure.ai.projects.models.AgentVersionDetails :raises ~azure.core.exceptions.HttpResponseError: @@ -129,6 +133,7 @@ async def create_version( definition: _models.AgentDefinition = _Unset, metadata: Optional[dict[str, str]] = None, description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, **kwargs: Any, ) -> _models.AgentVersionDetails: """Create a new agent version. @@ -154,6 +159,8 @@ async def create_version( :paramtype metadata: dict[str, str] :keyword description: A human-readable description of the agent. Default value is None. :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.projects.models.AgentBlueprintReference :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping :rtype: ~azure.ai.projects.models.AgentVersionDetails :raises ~azure.core.exceptions.HttpResponseError: @@ -174,6 +181,7 @@ async def create_version( definition=definition, metadata=metadata, description=description, + blueprint_reference=blueprint_reference, **kwargs, ) except HttpResponseError as exc: diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_evaluation_rules_async.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_evaluation_rules_async.py index a296493c469b..7e61eeb2866c 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_evaluation_rules_async.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_evaluation_rules_async.py @@ -13,9 +13,13 @@ from azure.core.tracing.decorator_async import distributed_trace_async from ._operations import EvaluationRulesOperations as GeneratedEvaluationRulesOperations, JSON from ... import models as _models -from ...operations._patch_agents import _PREVIEW_FEATURE_REQUIRED_CODE, _PREVIEW_FEATURE_ADDED_ERROR_MESSAGE from ...models._enums import _FoundryFeaturesOptInKeys -from ...models._patch import _FOUNDRY_FEATURES_HEADER_NAME, _has_header_case_insensitive +from ...models._patch import ( + _FOUNDRY_FEATURES_HEADER_NAME, + _has_header_case_insensitive, + _PREVIEW_FEATURE_REQUIRED_CODE, + _PREVIEW_FEATURE_ADDED_ERROR_MESSAGE, +) class EvaluationRulesOperations(GeneratedEvaluationRulesOperations): diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_models_async.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_models_async.py new file mode 100644 index 000000000000..bed6ccfae89d --- /dev/null +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_models_async.py @@ -0,0 +1,330 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" + +import asyncio # pylint: disable=do-not-import-asyncio +import logging +import os +from pathlib import Path +from typing import Any, Literal, Optional, Union, overload + +from azure.core.exceptions import ResourceNotFoundError +from azure.core.tracing.decorator_async import distributed_trace_async + +from ._operations import BetaModelsOperations as BetaModelsOperationsGenerated +from ...models._models import ( + ModelPendingUploadRequest, + ModelPendingUploadResponse, + ModelVersion, + PendingUploadType, +) + +logger = logging.getLogger(__name__) + + +class BetaModelsOperations(BetaModelsOperationsGenerated): + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.aio.AIProjectClient`'s + :attr:`beta.models ` attribute. + """ + + @staticmethod + def _extract_pending_upload_targets( + response: Union[ModelPendingUploadResponse, dict], + ) -> "tuple[str, str, Optional[str]]": + """Return ``(sas_uri, container_blob_uri, pending_upload_id)`` from a pending-upload response. + + The service currently returns the raw datastore-style payload + (``blobReferenceForConsumption`` / ``temporaryDataReferenceId``) for some + Foundry deployments rather than the SDK-modeled ``ModelPendingUploadResponse`` + shape (``blobReference`` / ``pendingUploadId``). Tolerate both wire + shapes so callers don't have to. + + :param response: The pending-upload response from the service. + :type response: ~azure.ai.projects.models.ModelPendingUploadResponse or dict + :return: A tuple of ``(sas_uri, container_blob_uri, pending_upload_id)``. + :rtype: tuple[str, str, str or None] + """ + payload = dict(response) if isinstance(response, dict) else response.as_dict() + + blob_ref = payload.get("blobReferenceForConsumption") or payload.get("blobReference") or {} + sas_uri = (blob_ref.get("credential") or {}).get("sasUri") + container_blob_uri = blob_ref.get("blobUri") + pending_upload_id = payload.get("temporaryDataReferenceId") or payload.get("pendingUploadId") + + if not sas_uri or not container_blob_uri: + raise ValueError("Could not locate SAS URI / blob URI in pending_upload response: " f"{payload!r}") + return sas_uri, container_blob_uri, pending_upload_id + + @staticmethod + def _validate_create_inputs( + *, + name: str, + version: str, + source: Union[str, "os.PathLike[str]"], + wait_for_commit: bool, + polling_timeout: float, + polling_interval: float, + ) -> Path: + """Validate ``create`` inputs up-front, before any service call. + + Returns the resolved ``Path`` for ``source``. Raises ``ValueError`` for + bad inputs. + + :keyword name: Name of the model to register. + :paramtype name: str + :keyword version: Version identifier for the model. + :paramtype version: str + :keyword source: Local file or directory containing the model weights. + :paramtype source: str or os.PathLike[str] + :keyword wait_for_commit: Whether to poll for commit completion. + :paramtype wait_for_commit: bool + :keyword polling_timeout: Total seconds to poll for commit completion. + :paramtype polling_timeout: float + :keyword polling_interval: Seconds between poll attempts. + :paramtype polling_interval: float + :return: The resolved ``Path`` for ``source``. + :rtype: pathlib.Path + """ + if not isinstance(name, str) or not name.strip(): + raise ValueError("`name` must be a non-empty string.") + if not isinstance(version, str) or not version.strip(): + raise ValueError("`version` must be a non-empty string.") + + source_path = Path(os.fspath(source)) + if not source_path.exists(): + raise ValueError(f"Upload source does not exist: {source_path}") + if source_path.is_dir() and not any(p.is_file() for p in source_path.rglob("*")): + raise ValueError(f"Upload source directory is empty: {source_path}") + if source_path.is_file() and source_path.stat().st_size == 0: + raise ValueError(f"Upload source file is empty: {source_path}") + + if wait_for_commit: + if polling_timeout <= 0: + raise ValueError("`polling_timeout` must be > 0 when `wait_for_commit` is True.") + if polling_interval <= 0: + raise ValueError("`polling_interval` must be > 0 when `wait_for_commit` is True.") + + return source_path + + @staticmethod + async def _upload_with_container_client(source: Path, sas_uri: str) -> None: + """Upload ``source`` to the SAS container using ``azure.storage.blob.aio.ContainerClient``. + + :param source: Local file or directory to upload. + :type source: pathlib.Path + :param sas_uri: SAS URI for the destination container. + :type sas_uri: str + :raises RuntimeError: If ``azure-storage-blob`` is not installed. + """ + try: + from azure.storage.blob.aio import ContainerClient # pylint: disable=import-outside-toplevel + except ImportError as ex: + raise RuntimeError( + "`azure-storage-blob` is required for the async `create` helper. " + "Install it with `pip install azure-storage-blob aiohttp`." + ) from ex + + if source.is_dir(): + files = [p for p in source.rglob("*") if p.is_file()] + if not files: + raise ValueError(f"Upload source directory is empty: {source}") + elif source.is_file(): + files = [source] + else: + raise ValueError(f"Upload source does not exist: {source}") + + # Don't log the SAS query string — it's a credential. + redacted = sas_uri.split("?", 1)[0] + "?" + logger.info("[create] uploading %d file(s) to %s", len(files), redacted) + + async with ContainerClient.from_container_url(sas_uri) as container_client: + for f in files: + rel = f.relative_to(source).as_posix() if source.is_dir() else f.name + with f.open("rb") as fp: + await container_client.upload_blob(name=rel, data=fp, overwrite=True) + logger.debug("[create] uploaded %s (%d bytes)", rel, f.stat().st_size) + + @overload + async def create( + self, + *, + name: str, + version: str, + source: Union[str, "os.PathLike[str]"], + weight_type: Optional[str] = None, + base_model: Optional[str] = None, + description: Optional[str] = None, + tags: Optional["dict[str, str]"] = None, + wait_for_commit: Literal[True] = True, + polling_timeout: float = 300.0, + polling_interval: float = 2.0, + **kwargs: Any, + ) -> ModelVersion: ... + + @overload + async def create( + self, + *, + name: str, + version: str, + source: Union[str, "os.PathLike[str]"], + weight_type: Optional[str] = None, + base_model: Optional[str] = None, + description: Optional[str] = None, + tags: Optional["dict[str, str]"] = None, + wait_for_commit: Literal[False], + polling_timeout: float = 300.0, + polling_interval: float = 2.0, + **kwargs: Any, + ) -> None: ... + + @distributed_trace_async + async def create( + self, + *, + name: str, + version: str, + source: Union[str, "os.PathLike[str]"], + weight_type: Optional[str] = None, + base_model: Optional[str] = None, + description: Optional[str] = None, + tags: Optional["dict[str, str]"] = None, + wait_for_commit: bool = True, + polling_timeout: float = 300.0, + polling_interval: float = 2.0, + **kwargs: Any, + ) -> Optional[ModelVersion]: + """Register a local model by running the full upload-first sequence (async). + + This wraps the three mandatory steps of the model-registration spec + into a single call: + + 1. :meth:`pending_upload` — provision a project-managed blob container + and obtain a SAS URI. + 2. Upload the local weight files to the SAS container using + :class:`azure.storage.blob.aio.ContainerClient`. + 3. :meth:`pending_create_version` — finalize registration with the + ``ModelVersion`` body (``blob_uri``, ``weight_type``, ``base_model``, + ``description``, ``tags``). + + Requires the ``azure-storage-blob`` package (with ``aiohttp``) for the + upload step. + + :keyword name: Name of the model to register. Required. + :paramtype name: str + :keyword version: Version identifier for the model. Required. + :paramtype version: str + :keyword source: Local file or directory containing the model weights. + If a directory, its contents are uploaded recursively to the SAS + container root. Required. + :paramtype source: str or os.PathLike[str] + :keyword weight_type: Optional weight type (e.g. ``"FullWeight"``, + ``"LoRA"``, ``"DraftModel"``). + :paramtype weight_type: str + :keyword base_model: Optional base model asset ID. + :paramtype base_model: str + :keyword description: Optional asset description. + :paramtype description: str + :keyword tags: Optional asset tags. + :paramtype tags: dict[str, str] + :keyword wait_for_commit: When True (default) poll :meth:`get` until + the committed ``ModelVersion`` is observable, and return it. + When False, return ``None`` after the async commit is accepted. + :paramtype wait_for_commit: bool + :keyword polling_timeout: Total seconds to poll for commit completion. + :paramtype polling_timeout: float + :keyword polling_interval: Seconds between poll attempts. + :paramtype polling_interval: float + :return: The committed :class:`~azure.ai.projects.models.ModelVersion` + when ``wait_for_commit`` is True, otherwise ``None``. + :rtype: ~azure.ai.projects.models.ModelVersion or None + :raises ValueError: If ``name``/``version`` are empty, ``source`` does + not exist or is empty, polling parameters are non-positive, or the + pending-upload response is missing the SAS / blob URI. + :raises RuntimeError: If ``azure-storage-blob`` is not installed or + the registration does not commit before ``polling_timeout`` elapses. + """ + # --- Step 0: validate inputs up-front -------------------------------- + source_path = self._validate_create_inputs( + name=name, + version=version, + source=source, + wait_for_commit=wait_for_commit, + polling_timeout=polling_timeout, + polling_interval=polling_interval, + ) + + # --- Step 1: StartPendingUpload -------------------------------------- + logger.info( + "[create] step 1/3 pending_upload(name=%r, version=%r)", + name, + version, + ) + pending = await self.pending_upload( + name=name, + version=version, + pending_upload_request=ModelPendingUploadRequest( + pending_upload_type=PendingUploadType.TEMPORARY_BLOB_REFERENCE, + ), + **kwargs, + ) + sas_uri, container_blob_uri, pending_upload_id = self._extract_pending_upload_targets(pending) + logger.info( + "[create] pending_upload_id=%s blob_uri=%s", + pending_upload_id, + container_blob_uri, + ) + + # --- Step 2: Upload via async ContainerClient ------------------------ + logger.info("[create] step 2/3 async upload from %s", source_path) + await self._upload_with_container_client(source_path, sas_uri) + + # --- Step 3: Commit registration ------------------------------------- + model_version_body = ModelVersion( + blob_uri=container_blob_uri, + weight_type=weight_type, + base_model=base_model, + description=description, + tags=tags or {}, + ) + logger.info( + "[create] step 3/3 pending_create_version(name=%r, version=%r)", + name, + version, + ) + await self.pending_create_version(name=name, version=version, model_version=model_version_body, **kwargs) + + if not wait_for_commit: + return None + + # The async op returns 202; the service materializes the ModelVersion + # asynchronously. Poll get() until it appears or we time out. + import time # pylint: disable=import-outside-toplevel + + deadline = time.monotonic() + polling_timeout + last_exc: Optional[BaseException] = None + while True: + try: + return await self.get(name=name, version=version, **kwargs) + except ResourceNotFoundError as ex: + last_exc = ex + if time.monotonic() >= deadline: + raise RuntimeError( + f"Model {name!r}@{version!r} did not appear within " + f"{polling_timeout}s after pending_create_version." + ) from last_exc + await asyncio.sleep(polling_interval) + + +__all__ = ["BetaModelsOperations"] diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_sessions_async.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_sessions_async.py index c4257fec3242..dd1427541a65 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_sessions_async.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_sessions_async.py @@ -34,7 +34,7 @@ async def upload_session_file( # type: ignore[override] *, path: str, **kwargs: Any, - ) -> _models.SessionFileWriteResponse: + ) -> _models.SessionFileWriteResult: """Upload a file to the session sandbox. Accepts either a ``bytes`` buffer or a local file path (``str``). @@ -52,9 +52,9 @@ async def upload_session_file( # type: ignore[override] :keyword path: The destination file path within the sandbox, relative to the session home directory. Required. :paramtype path: str - :return: SessionFileWriteResponse. The SessionFileWriteResponse is compatible with + :return: SessionFileWriteResult. The SessionFileWriteResult is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SessionFileWriteResponse + :rtype: ~azure.ai.projects.models.SessionFileWriteResult :raises ~azure.core.exceptions.HttpResponseError: :raises FileNotFoundError: If *content_or_file_path* is a ``str`` and the file does not exist. """ 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 8dfca8b30a03..a9a5f613cdaa 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 @@ -21,21 +21,26 @@ AgentCardSkill, AgentClusterInsightRequest, AgentClusterInsightResult, + AgentDataGenerationJobSource, AgentDefinition, AgentDetails, - AgentEndpoint, AgentEndpointAuthorizationScheme, + AgentEndpointConfig, + AgentEvaluatorGenerationJobSource, + AgentIdentifier, AgentIdentity, AgentObjectVersions, AgentSessionResource, AgentTaxonomyInput, AgentVersionDetails, AgenticIdentityPreviewCredentials, + AgentsPagedResultOptimizationCandidate, ApiError, ApiErrorResponse, ApiKeyCredentials, ApplyPatchToolParam, ApproximateLocation, + ArtifactProfile, AutoCodeInterpreterToolParam, AzureAIAgentTarget, AzureAIModelTarget, @@ -62,6 +67,10 @@ BrowserAutomationPreviewTool, BrowserAutomationToolConnectionParameters, BrowserAutomationToolParameters, + CandidateDeployConfig, + CandidateFileInfo, + CandidateMetadata, + CandidateResults, CaptureStructuredOutputsTool, ChartCoordinate, ChatSummaryMemoryItem, @@ -83,20 +92,43 @@ ContainerSkill, ContinuousEvaluationRuleAction, CosmosDBIndex, + CreateAgentVersionFromCodeContent, + CreateAgentVersionFromCodeMetadata, + CreateAsyncResponse, + CreateSkillVersionFromFilesBody, CronTrigger, CustomCredential, CustomGrammarFormatParam, + CustomRoutineTrigger, CustomTextFormatParam, CustomToolParam, CustomToolParamFormat, DailyRecurrenceSchedule, + DataGenerationJob, + DataGenerationJobInputs, + DataGenerationJobOptions, + DataGenerationJobOutput, + DataGenerationJobOutputOptions, + DataGenerationJobResult, + DataGenerationJobSource, + DataGenerationModelOptions, + DataGenerationTokenUsage, DatasetCredential, + DatasetDataGenerationJobOutput, + DatasetEvaluatorGenerationJobSource, + DatasetInfo, + DatasetRef, + DatasetReference, DatasetVersion, DeleteAgentResponse, DeleteAgentVersionResponse, + DeleteMemoryResult, DeleteMemoryStoreResult, - DeleteSkillResponse, + DeleteSkillResult, + DeleteSkillVersionResponse, Deployment, + Dimension, + DispatchRoutineResult, EmbeddingConfiguration, EntraAuthorizationScheme, EntraIDCredentials, @@ -114,22 +146,35 @@ EvaluationRunClusterInsightRequest, EvaluationRunClusterInsightResult, EvaluationScheduleTask, + EvaluationTarget, EvaluationTaxonomy, EvaluationTaxonomyInput, + EvaluatorCredentialRequest, EvaluatorDefinition, + EvaluatorGenerationArtifacts, + EvaluatorGenerationInputs, + EvaluatorGenerationJob, + EvaluatorGenerationJobSource, + EvaluatorGenerationTokenUsage, EvaluatorMetric, EvaluatorVersion, + ExternalAgentDefinition, FabricDataAgentToolParameters, + FabricIQPreviewTool, FieldMapping, + FileDataGenerationJobOutput, + FileDataGenerationJobSource, FileDatasetVersion, FileSearchTool, FixedRatioVersionSelectionRule, FolderDatasetVersion, + FoundryModelWarning, FunctionShellToolParam, FunctionShellToolParamEnvironment, FunctionShellToolParamEnvironmentContainerReferenceParam, FunctionShellToolParamEnvironmentLocalEnvironmentParam, FunctionTool, + GitHubIssueRoutineTrigger, HeaderIsolationKeySource, HeaderTelemetryEndpointAuth, HostedAgentDefinition, @@ -150,9 +195,14 @@ InsightScheduleTask, InsightSummary, InsightsMetadata, + InvokeAgentInvocationsApiDispatchPayload, + InvokeAgentInvocationsApiRoutineAction, + InvokeAgentResponsesApiDispatchPayload, + InvokeAgentResponsesApiRoutineAction, IsolationKeySource, LocalShellToolParam, LocalSkillParam, + LoraConfig, MCPTool, MCPToolFilter, MCPToolRequireApproval, @@ -173,9 +223,14 @@ MemoryStoreUpdateCompletedResult, MemoryStoreUpdateResult, MicrosoftFabricPreviewTool, + ModelCredentialRequest, ModelDeployment, ModelDeploymentSku, + ModelPendingUploadRequest, + ModelPendingUploadResponse, ModelSamplingParams, + ModelSourceData, + ModelVersion, MonthlyRecurrenceSchedule, NoAuthenticationCredentials, OneTimeTrigger, @@ -188,12 +243,26 @@ OpenApiProjectConnectionAuthDetails, OpenApiProjectConnectionSecurityScheme, OpenApiTool, + OptimizationAgentDefinition, + OptimizationCandidate, + OptimizationJob, + OptimizationJobInputs, + OptimizationJobProgress, + OptimizationJobResult, + OptimizationOptions, + OptimizationTaskResult, OtlpTelemetryEndpoint, PendingUploadRequest, PendingUploadResponse, + ProceduralMemoryItem, + PromoteCandidateRequest, + PromoteCandidateResponse, + PromotionInfo, PromptAgentDefinition, PromptAgentDefinitionTextOptions, PromptBasedEvaluatorDefinition, + PromptDataGenerationJobSource, + PromptEvaluatorGenerationJobSource, ProtocolVersionRecord, RaiConfig, RankingOptions, @@ -201,26 +270,35 @@ RecurrenceSchedule, RecurrenceTrigger, RedTeam, + RedTeamTargetConfig, ResponseUsageInputTokensDetails, ResponseUsageOutputTokensDetails, + Routine, + RoutineAction, + RoutineDispatchPayload, + RoutineRun, + RoutineTrigger, + RubricBasedEvaluatorDefinition, SASCredentials, Schedule, + ScheduleRoutineTrigger, ScheduleRun, ScheduleTask, SessionDirectoryEntry, - SessionDirectoryListResponse, - SessionFileWriteResponse, + SessionFileWriteResult, SessionLogEvent, SharepointGroundingToolParameters, SharepointPreviewTool, - SkillObject, + SimpleQnADataGenerationJobOptions, + SkillDetails, + SkillInlineContent, SkillReferenceParam, + SkillVersion, SpecificApplyPatchParam, SpecificFunctionShellParam, StructuredInputDefinition, StructuredOutputDefinition, - Target, - TargetConfig, + SystemDataV3, TaxonomyCategory, TaxonomySubCategory, TelemetryConfig, @@ -230,6 +308,7 @@ TextResponseFormatJsonObject, TextResponseFormatJsonSchema, TextResponseFormatText, + TimerRoutineTrigger, Tool, ToolChoiceAllowed, ToolChoiceCodeInterpreter, @@ -242,12 +321,21 @@ ToolChoiceParam, ToolChoiceWebSearchPreview, ToolChoiceWebSearchPreview20250311, + ToolConfig, ToolDescription, ToolProjectConnection, + ToolUseFineTuningDataGenerationJobOptions, ToolboxObject, ToolboxPolicies, + ToolboxSearchPreviewTool, + ToolboxSkill, + ToolboxSkillReference, ToolboxVersionObject, + TracesDataGenerationJobOptions, + TracesDataGenerationJobSource, + TracesEvaluatorGenerationJobSource, Trigger, + UpdateModelVersionRequest, UpdateToolboxRequest, UserProfileMemoryItem, VersionIndicator, @@ -261,7 +349,6 @@ WebSearchToolFilters, WeeklyRecurrenceSchedule, WorkIQPreviewTool, - WorkIQPreviewToolParameters, WorkflowAgentDefinition, ) @@ -276,6 +363,7 @@ AgentVersionStatus, AttackStrategy, AzureAISearchQueryType, + CodeDependencyResolution, ComputerEnvironment, ConnectionType, ContainerMemoryLimit, @@ -283,24 +371,37 @@ ContainerSkillType, CredentialType, CustomToolParamFormatType, + DataGenerationJobOutputType, + DataGenerationJobScenario, + DataGenerationJobSourceType, + DataGenerationJobType, DatasetType, DayOfWeek, DeploymentType, + EvaluationLevel, EvaluationRuleActionType, EvaluationRuleEventType, EvaluationTaxonomyInputType, EvaluatorCategory, EvaluatorDefinitionType, + EvaluatorGenerationJobSourceType, EvaluatorMetricDirection, EvaluatorMetricType, EvaluatorType, + FoundryModelArtifactProfileCategory, + FoundryModelArtifactProfileSignal, + FoundryModelSourceType, + FoundryModelWarningCode, + FoundryModelWeightType, FunctionShellToolParamEnvironmentType, + GitHubIssueEvent, GrammarSyntax1, ImageGenAction, IndexType, InputFidelity, InsightType, IsolationKeySourceKind, + JobStatus, MemoryItemKind, MemoryOperationKind, MemoryStoreKind, @@ -313,11 +414,17 @@ RankerVersionType, RecurrenceType, RiskCategory, + RoutineActionType, + RoutineAttemptSource, + RoutineDispatchPayloadType, + RoutineRunPhase, + RoutineTriggerType, SampleType, ScheduleProvisioningStatus, ScheduleTaskType, SearchContextSize, SessionLogEventType, + SimpleQnAFineTuningQuestionType, TelemetryDataKind, TelemetryEndpointAuthType, TelemetryEndpointKind, @@ -342,21 +449,26 @@ "AgentCardSkill", "AgentClusterInsightRequest", "AgentClusterInsightResult", + "AgentDataGenerationJobSource", "AgentDefinition", "AgentDetails", - "AgentEndpoint", "AgentEndpointAuthorizationScheme", + "AgentEndpointConfig", + "AgentEvaluatorGenerationJobSource", + "AgentIdentifier", "AgentIdentity", "AgentObjectVersions", "AgentSessionResource", "AgentTaxonomyInput", "AgentVersionDetails", "AgenticIdentityPreviewCredentials", + "AgentsPagedResultOptimizationCandidate", "ApiError", "ApiErrorResponse", "ApiKeyCredentials", "ApplyPatchToolParam", "ApproximateLocation", + "ArtifactProfile", "AutoCodeInterpreterToolParam", "AzureAIAgentTarget", "AzureAIModelTarget", @@ -383,6 +495,10 @@ "BrowserAutomationPreviewTool", "BrowserAutomationToolConnectionParameters", "BrowserAutomationToolParameters", + "CandidateDeployConfig", + "CandidateFileInfo", + "CandidateMetadata", + "CandidateResults", "CaptureStructuredOutputsTool", "ChartCoordinate", "ChatSummaryMemoryItem", @@ -404,20 +520,43 @@ "ContainerSkill", "ContinuousEvaluationRuleAction", "CosmosDBIndex", + "CreateAgentVersionFromCodeContent", + "CreateAgentVersionFromCodeMetadata", + "CreateAsyncResponse", + "CreateSkillVersionFromFilesBody", "CronTrigger", "CustomCredential", "CustomGrammarFormatParam", + "CustomRoutineTrigger", "CustomTextFormatParam", "CustomToolParam", "CustomToolParamFormat", "DailyRecurrenceSchedule", + "DataGenerationJob", + "DataGenerationJobInputs", + "DataGenerationJobOptions", + "DataGenerationJobOutput", + "DataGenerationJobOutputOptions", + "DataGenerationJobResult", + "DataGenerationJobSource", + "DataGenerationModelOptions", + "DataGenerationTokenUsage", "DatasetCredential", + "DatasetDataGenerationJobOutput", + "DatasetEvaluatorGenerationJobSource", + "DatasetInfo", + "DatasetRef", + "DatasetReference", "DatasetVersion", "DeleteAgentResponse", "DeleteAgentVersionResponse", + "DeleteMemoryResult", "DeleteMemoryStoreResult", - "DeleteSkillResponse", + "DeleteSkillResult", + "DeleteSkillVersionResponse", "Deployment", + "Dimension", + "DispatchRoutineResult", "EmbeddingConfiguration", "EntraAuthorizationScheme", "EntraIDCredentials", @@ -435,22 +574,35 @@ "EvaluationRunClusterInsightRequest", "EvaluationRunClusterInsightResult", "EvaluationScheduleTask", + "EvaluationTarget", "EvaluationTaxonomy", "EvaluationTaxonomyInput", + "EvaluatorCredentialRequest", "EvaluatorDefinition", + "EvaluatorGenerationArtifacts", + "EvaluatorGenerationInputs", + "EvaluatorGenerationJob", + "EvaluatorGenerationJobSource", + "EvaluatorGenerationTokenUsage", "EvaluatorMetric", "EvaluatorVersion", + "ExternalAgentDefinition", "FabricDataAgentToolParameters", + "FabricIQPreviewTool", "FieldMapping", + "FileDataGenerationJobOutput", + "FileDataGenerationJobSource", "FileDatasetVersion", "FileSearchTool", "FixedRatioVersionSelectionRule", "FolderDatasetVersion", + "FoundryModelWarning", "FunctionShellToolParam", "FunctionShellToolParamEnvironment", "FunctionShellToolParamEnvironmentContainerReferenceParam", "FunctionShellToolParamEnvironmentLocalEnvironmentParam", "FunctionTool", + "GitHubIssueRoutineTrigger", "HeaderIsolationKeySource", "HeaderTelemetryEndpointAuth", "HostedAgentDefinition", @@ -471,9 +623,14 @@ "InsightScheduleTask", "InsightSummary", "InsightsMetadata", + "InvokeAgentInvocationsApiDispatchPayload", + "InvokeAgentInvocationsApiRoutineAction", + "InvokeAgentResponsesApiDispatchPayload", + "InvokeAgentResponsesApiRoutineAction", "IsolationKeySource", "LocalShellToolParam", "LocalSkillParam", + "LoraConfig", "MCPTool", "MCPToolFilter", "MCPToolRequireApproval", @@ -494,9 +651,14 @@ "MemoryStoreUpdateCompletedResult", "MemoryStoreUpdateResult", "MicrosoftFabricPreviewTool", + "ModelCredentialRequest", "ModelDeployment", "ModelDeploymentSku", + "ModelPendingUploadRequest", + "ModelPendingUploadResponse", "ModelSamplingParams", + "ModelSourceData", + "ModelVersion", "MonthlyRecurrenceSchedule", "NoAuthenticationCredentials", "OneTimeTrigger", @@ -509,12 +671,26 @@ "OpenApiProjectConnectionAuthDetails", "OpenApiProjectConnectionSecurityScheme", "OpenApiTool", + "OptimizationAgentDefinition", + "OptimizationCandidate", + "OptimizationJob", + "OptimizationJobInputs", + "OptimizationJobProgress", + "OptimizationJobResult", + "OptimizationOptions", + "OptimizationTaskResult", "OtlpTelemetryEndpoint", "PendingUploadRequest", "PendingUploadResponse", + "ProceduralMemoryItem", + "PromoteCandidateRequest", + "PromoteCandidateResponse", + "PromotionInfo", "PromptAgentDefinition", "PromptAgentDefinitionTextOptions", "PromptBasedEvaluatorDefinition", + "PromptDataGenerationJobSource", + "PromptEvaluatorGenerationJobSource", "ProtocolVersionRecord", "RaiConfig", "RankingOptions", @@ -522,26 +698,35 @@ "RecurrenceSchedule", "RecurrenceTrigger", "RedTeam", + "RedTeamTargetConfig", "ResponseUsageInputTokensDetails", "ResponseUsageOutputTokensDetails", + "Routine", + "RoutineAction", + "RoutineDispatchPayload", + "RoutineRun", + "RoutineTrigger", + "RubricBasedEvaluatorDefinition", "SASCredentials", "Schedule", + "ScheduleRoutineTrigger", "ScheduleRun", "ScheduleTask", "SessionDirectoryEntry", - "SessionDirectoryListResponse", - "SessionFileWriteResponse", + "SessionFileWriteResult", "SessionLogEvent", "SharepointGroundingToolParameters", "SharepointPreviewTool", - "SkillObject", + "SimpleQnADataGenerationJobOptions", + "SkillDetails", + "SkillInlineContent", "SkillReferenceParam", + "SkillVersion", "SpecificApplyPatchParam", "SpecificFunctionShellParam", "StructuredInputDefinition", "StructuredOutputDefinition", - "Target", - "TargetConfig", + "SystemDataV3", "TaxonomyCategory", "TaxonomySubCategory", "TelemetryConfig", @@ -551,6 +736,7 @@ "TextResponseFormatJsonObject", "TextResponseFormatJsonSchema", "TextResponseFormatText", + "TimerRoutineTrigger", "Tool", "ToolChoiceAllowed", "ToolChoiceCodeInterpreter", @@ -563,12 +749,21 @@ "ToolChoiceParam", "ToolChoiceWebSearchPreview", "ToolChoiceWebSearchPreview20250311", + "ToolConfig", "ToolDescription", "ToolProjectConnection", + "ToolUseFineTuningDataGenerationJobOptions", "ToolboxObject", "ToolboxPolicies", + "ToolboxSearchPreviewTool", + "ToolboxSkill", + "ToolboxSkillReference", "ToolboxVersionObject", + "TracesDataGenerationJobOptions", + "TracesDataGenerationJobSource", + "TracesEvaluatorGenerationJobSource", "Trigger", + "UpdateModelVersionRequest", "UpdateToolboxRequest", "UserProfileMemoryItem", "VersionIndicator", @@ -582,7 +777,6 @@ "WebSearchToolFilters", "WeeklyRecurrenceSchedule", "WorkIQPreviewTool", - "WorkIQPreviewToolParameters", "WorkflowAgentDefinition", "AgentBlueprintReferenceType", "AgentEndpointAuthorizationSchemeType", @@ -594,6 +788,7 @@ "AgentVersionStatus", "AttackStrategy", "AzureAISearchQueryType", + "CodeDependencyResolution", "ComputerEnvironment", "ConnectionType", "ContainerMemoryLimit", @@ -601,24 +796,37 @@ "ContainerSkillType", "CredentialType", "CustomToolParamFormatType", + "DataGenerationJobOutputType", + "DataGenerationJobScenario", + "DataGenerationJobSourceType", + "DataGenerationJobType", "DatasetType", "DayOfWeek", "DeploymentType", + "EvaluationLevel", "EvaluationRuleActionType", "EvaluationRuleEventType", "EvaluationTaxonomyInputType", "EvaluatorCategory", "EvaluatorDefinitionType", + "EvaluatorGenerationJobSourceType", "EvaluatorMetricDirection", "EvaluatorMetricType", "EvaluatorType", + "FoundryModelArtifactProfileCategory", + "FoundryModelArtifactProfileSignal", + "FoundryModelSourceType", + "FoundryModelWarningCode", + "FoundryModelWeightType", "FunctionShellToolParamEnvironmentType", + "GitHubIssueEvent", "GrammarSyntax1", "ImageGenAction", "IndexType", "InputFidelity", "InsightType", "IsolationKeySourceKind", + "JobStatus", "MemoryItemKind", "MemoryOperationKind", "MemoryStoreKind", @@ -631,11 +839,17 @@ "RankerVersionType", "RecurrenceType", "RiskCategory", + "RoutineActionType", + "RoutineAttemptSource", + "RoutineDispatchPayloadType", + "RoutineRunPhase", + "RoutineTriggerType", "SampleType", "ScheduleProvisioningStatus", "ScheduleTaskType", "SearchContextSize", "SessionLogEventType", + "SimpleQnAFineTuningQuestionType", "TelemetryDataKind", "TelemetryEndpointAuthType", "TelemetryEndpointKind", 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 588927c00421..fb8d84b06bce 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 @@ -1,3 +1,4 @@ +# pylint: disable=too-many-lines # coding=utf-8 # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. @@ -17,17 +18,17 @@ class _AgentDefinitionOptInKeys(str, Enum, metaclass=CaseInsensitiveEnumMeta): """HOSTED_AGENTS_V1_PREVIEW.""" WORKFLOW_AGENTS_V1_PREVIEW = "WorkflowAgents=V1Preview" """WORKFLOW_AGENTS_V1_PREVIEW.""" - CONTAINER_AGENTS_V1_PREVIEW = "ContainerAgents=V1Preview" - """CONTAINER_AGENTS_V1_PREVIEW.""" AGENT_ENDPOINT_V1_PREVIEW = "AgentEndpoints=V1Preview" """AGENT_ENDPOINT_V1_PREVIEW.""" + CODE_AGENTS_V1_PREVIEW = "CodeAgents=V1Preview" + """CODE_AGENTS_V1_PREVIEW.""" + EXTERNAL_AGENTS_V1_PREVIEW = "ExternalAgents=V1Preview" + """EXTERNAL_AGENTS_V1_PREVIEW.""" class _FoundryFeaturesOptInKeys(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Type of _FoundryFeaturesOptInKeys.""" - SKILLS_V1_PREVIEW = "Skills=V1Preview" - """SKILLS_V1_PREVIEW.""" EVALUATIONS_V1_PREVIEW = "Evaluations=V1Preview" """EVALUATIONS_V1_PREVIEW.""" SCHEDULES_V1_PREVIEW = "Schedules=V1Preview" @@ -38,8 +39,18 @@ class _FoundryFeaturesOptInKeys(str, Enum, metaclass=CaseInsensitiveEnumMeta): """INSIGHTS_V1_PREVIEW.""" MEMORY_STORES_V1_PREVIEW = "MemoryStores=V1Preview" """MEMORY_STORES_V1_PREVIEW.""" + ROUTINES_V1_PREVIEW = "Routines=V1Preview" + """ROUTINES_V1_PREVIEW.""" TOOLBOXES_V1_PREVIEW = "Toolboxes=V1Preview" """TOOLBOXES_V1_PREVIEW.""" + SKILLS_V1_PREVIEW = "Skills=V1Preview" + """SKILLS_V1_PREVIEW.""" + DATA_GENERATION_JOBS_V1_PREVIEW = "DataGenerationJobs=V1Preview" + """DATA_GENERATION_JOBS_V1_PREVIEW.""" + MODELS_V1_PREVIEW = "Models=V1Preview" + """MODELS_V1_PREVIEW.""" + AGENTS_OPTIMIZATION_V1_PREVIEW = "AgentsOptimization=V1Preview" + """AGENTS_OPTIMIZATION_V1_PREVIEW.""" class AgentBlueprintReferenceType(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -69,8 +80,12 @@ class AgentEndpointProtocol(str, Enum, metaclass=CaseInsensitiveEnumMeta): """RESPONSES.""" A2A = "a2a" """A2A.""" + MCP = "mcp" + """MCP.""" INVOCATIONS = "invocations" """INVOCATIONS.""" + INVOCATIONS_WS = "invocations_ws" + """WebSocket-based protocol for hosted voice and real-time streaming agents.""" class AgentKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -82,6 +97,8 @@ class AgentKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): """HOSTED.""" WORKFLOW = "workflow" """WORKFLOW.""" + EXTERNAL = "external" + """EXTERNAL.""" class AgentObjectType(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -106,8 +123,12 @@ class AgentProtocol(str, Enum, metaclass=CaseInsensitiveEnumMeta): """ACTIVITY_PROTOCOL.""" RESPONSES = "responses" """RESPONSES.""" + MCP = "mcp" + """MCP.""" INVOCATIONS = "invocations" """INVOCATIONS.""" + INVOCATIONS_WS = "invocations_ws" + """WebSocket-based protocol for hosted voice and real-time streaming agents.""" class AgentSessionStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -232,6 +253,16 @@ class AzureAISearchQueryType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Query type ``vector_semantic_hybrid``.""" +class CodeDependencyResolution(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """How package dependencies are resolved at deployment time for a code-based hosted agent.""" + + BUNDLED = "bundled" + """The caller has bundled all dependencies into the uploaded zip; the service performs no remote + build.""" + REMOTE_BUILD = "remote_build" + """The service builds dependencies remotely from the manifest included in the uploaded zip.""" + + class ComputerEnvironment(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Type of ComputerEnvironment.""" @@ -329,6 +360,50 @@ class CustomToolParamFormatType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """GRAMMAR.""" +class DataGenerationJobOutputType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The supported output file types for a data generation job.""" + + FILE = "file" + """The generated data is an Azure OpenAI File.""" + DATASET = "dataset" + """The generated data is a Dataset.""" + + +class DataGenerationJobScenario(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The supported scenarios for a data generation job.""" + + SUPERVISED_FINETUNING = "supervised_finetuning" + """Supervised Fine-tuning scenario.""" + REINFORCEMENT_FINETUNING = "reinforcement_finetuning" + """Reinforcement Fine-tuning scenario.""" + EVALUATION = "evaluation" + """Evaluation scenario.""" + + +class DataGenerationJobSourceType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The supported source types for data generation jobs.""" + + PROMPT = "prompt" + """Prompt source — inline text provided by the user.""" + AGENT = "agent" + """Agent source — references an agent.""" + TRACES = "traces" + """Traces source — conversation traces from Application Insights.""" + FILE = "file" + """File source — Azure OpenAI file.""" + + +class DataGenerationJobType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The supported data generation job types.""" + + SIMPLE_QNA = "simple_qna" + """Simple question and answers between user and agent.""" + TRACES = "traces" + """Single turn query and response from agent traces.""" + TOOL_USE = "tool_use" + """Tool calling conversation between user and agent.""" + + class DatasetType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Enum to determine the type of data.""" @@ -364,6 +439,15 @@ class DeploymentType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Model deployment.""" +class EvaluationLevel(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The level at which evaluation is performed.""" + + TURN = "turn" + """Evaluation is performed at the turn level.""" + CONVERSATION = "conversation" + """Evaluation is performed at the conversation level.""" + + class EvaluationRuleActionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Type of the evaluation action.""" @@ -415,6 +499,22 @@ class EvaluatorDefinitionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Service-based evaluator.""" OPENAI_GRADERS = "openai_graders" """OpenAI graders.""" + RUBRIC = "rubric" + """Rubric-based evaluator definition. Stores dimensions (the scoring blueprint) for both quality + and safety evaluators. Can be created via the generate API or manually via createVersion.""" + + +class EvaluatorGenerationJobSourceType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The supported source types for evaluator generation jobs.""" + + PROMPT = "prompt" + """Prompt source — inline text provided by the user.""" + AGENT = "agent" + """Agent source — references an agent to fetch instructions and metadata from.""" + TRACES = "traces" + """Traces source — conversation traces from Application Insights.""" + DATASET = "dataset" + """Dataset source — reference to a dataset.""" class EvaluatorMetricDirection(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -450,6 +550,61 @@ class EvaluatorType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Custom evaluator.""" +class FoundryModelArtifactProfileCategory(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The artifact profile category.""" + + DATA_ONLY = "DataOnly" + """Data only artifacts.""" + RUNTIME_DEPENDENT = "RuntimeDependent" + """Runtime dependent artifacts.""" + UNKNOWN = "Unknown" + """Unknown category.""" + + +class FoundryModelArtifactProfileSignal(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Signals detected in the model artifact.""" + + PICKLE_DESERIALIZATION = "PickleDeserialization" + """Pickle deserialization detected.""" + CUSTOM_PYTHON_CODE = "CustomPythonCode" + """Custom Python code detected.""" + DYNAMIC_OPS = "DynamicOps" + """Dynamic operations detected.""" + NATIVE_BINARY = "NativeBinary" + """Native binary code detected.""" + UNKNOWN_FORMAT = "UnknownFormat" + """Unknown format detected.""" + + +class FoundryModelSourceType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The source type of the model.""" + + LOCAL_UPLOAD = "LocalUpload" + """Model was uploaded locally.""" + TRAINING_JOB = "TrainingJob" + """Model was produced by a training job.""" + + +class FoundryModelWarningCode(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Warning code for model artifacts.""" + + RUNTIME_DEPENDENT_ARTIFACT = "RuntimeDependentArtifact" + """Runtime dependent artifact warning.""" + UNCLASSIFIED_ARTIFACT = "UnclassifiedArtifact" + """Unclassified artifact warning.""" + + +class FoundryModelWeightType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The weight type of the model.""" + + FULL_WEIGHT = "FullWeight" + """Full weight model.""" + LO_RA = "LoRA" + """LoRA adapter weights.""" + DRAFT_MODEL = "DraftModel" + """Draft model weights.""" + + class FunctionShellToolParamEnvironmentType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Type of FunctionShellToolParamEnvironmentType.""" @@ -461,6 +616,15 @@ class FunctionShellToolParamEnvironmentType(str, Enum, metaclass=CaseInsensitive """CONTAINER_REFERENCE.""" +class GitHubIssueEvent(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Known GitHub issue events that can fire a routine.""" + + OPENED = "opened" + """The routine fires when a GitHub issue is opened.""" + CLOSED = "closed" + """The routine fires when a GitHub issue is closed.""" + + class GrammarSyntax1(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Type of GrammarSyntax1.""" @@ -525,6 +689,21 @@ class IsolationKeySourceKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): """HEADER.""" +class JobStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Extensible status values shared by Foundry jobs.""" + + QUEUED = "queued" + """Job is waiting to start.""" + IN_PROGRESS = "in_progress" + """Job is actively processing.""" + SUCCEEDED = "succeeded" + """Job completed successfully.""" + FAILED = "failed" + """Job failed.""" + CANCELLED = "cancelled" + """Job was cancelled by the caller.""" + + class MemoryItemKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Memory item kind.""" @@ -532,6 +711,8 @@ class MemoryItemKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): """User profile information extracted from conversations.""" CHAT_SUMMARY = "chat_summary" """Summary of chat conversations.""" + PROCEDURAL = "procedural" + """Routine procedures extracted from conversations.""" class MemoryOperationKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -561,6 +742,8 @@ class MemoryStoreObjectType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """MEMORY_STORE_DELETED.""" MEMORY_STORE_SCOPE_DELETED = "memory_store.scope.deleted" """MEMORY_STORE_SCOPE_DELETED.""" + MEMORY_DELETED = "memory_store.item.deleted" + """MEMORY_DELETED.""" class MemoryStoreUpdateStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -624,7 +807,10 @@ class PendingUploadType(str, Enum, metaclass=CaseInsensitiveEnumMeta): NONE = "None" """No pending upload.""" BLOB_REFERENCE = "BlobReference" - """Blob Reference is the only supported type.""" + """Deprecated: the service never read this value and silently ignored it. Use + TemporaryBlobReference instead.""" + TEMPORARY_BLOB_REFERENCE = "TemporaryBlobReference" + """Temporary blob reference.""" class RankerVersionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -674,6 +860,65 @@ class RiskCategory(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Represents content that involves task adherence.""" +class RoutineActionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The discriminator values supported for routine actions.""" + + INVOKE_AGENT_RESPONSES_API = "invoke_agent_responses_api" + """Dispatches through the responses API.""" + INVOKE_AGENT_INVOCATIONS_API = "invoke_agent_invocations_api" + """Dispatches through the raw invocations API.""" + + +class RoutineAttemptSource(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Known source paths that can produce a routine run.""" + + EVENT_FIRE = "event_fire" + """A dispatch fired from an event delivery.""" + MANUAL_DISPATCH = "manual_dispatch" + """A dispatch executed synchronously by a direct request.""" + QUEUED_DISPATCH = "queued_dispatch" + """A dispatch executed asynchronously from the dispatch queue.""" + SCHEDULE_DELIVERY = "schedule_delivery" + """A dispatch fired from a schedule delivery.""" + TIMER_DELIVERY = "timer_delivery" + """A dispatch fired from a timer delivery.""" + + +class RoutineDispatchPayloadType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The discriminator values supported for manual routine dispatch payloads.""" + + INVOKE_AGENT_RESPONSES_API = "invoke_agent_responses_api" + """A manual payload for a responses API routine dispatch.""" + INVOKE_AGENT_INVOCATIONS_API = "invoke_agent_invocations_api" + """A manual payload for an invocations API routine dispatch.""" + + +class RoutineRunPhase(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Known lifecycle phases recorded for a routine run.""" + + QUEUED = "queued" + """The run is queued for dispatch.""" + DISPATCHING = "dispatching" + """The run is currently being dispatched.""" + COMPLETED = "completed" + """The run finished successfully.""" + FAILED = "failed" + """The run finished with an error.""" + + +class RoutineTriggerType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The discriminator values supported for routine triggers.""" + + CUSTOM = "custom" + """A custom event trigger.""" + GITHUB_ISSUE = "github_issue" + """A GitHub issue trigger.""" + SCHEDULE = "schedule" + """A recurring cron-based trigger.""" + TIMER = "timer" + """A one-shot timer trigger.""" + + class SampleType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """The type of sample used in the analysis.""" @@ -725,6 +970,15 @@ class SessionLogEventType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """A log line from the agent session container.""" +class SimpleQnAFineTuningQuestionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The supported question types for SimpleQnA data generation jobs used for fine-tuning scenarios.""" + + SHORT_ANSWER = "short_answer" + """Short answer question type.""" + LONG_ANSWER = "long_answer" + """Long answer question type.""" + + class TelemetryDataKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): """The type of telemetry data to export.""" @@ -840,6 +1094,10 @@ class ToolType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """MEMORY_SEARCH_PREVIEW.""" WORK_IQ_PREVIEW = "work_iq_preview" """WORK_IQ_PREVIEW.""" + FABRIC_IQ_PREVIEW = "fabric_iq_preview" + """FABRIC_IQ_PREVIEW.""" + TOOLBOX_SEARCH_PREVIEW = "toolbox_search_preview" + """TOOLBOX_SEARCH_PREVIEW.""" AZURE_AI_SEARCH = "azure_ai_search" """AZURE_AI_SEARCH.""" AZURE_FUNCTION = "azure_function" 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 f2da2ee9bdb2..5605a3bf0038 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 @@ -12,6 +12,7 @@ from typing import Any, Literal, Mapping, Optional, TYPE_CHECKING, Union, overload from .._utils.model_base import Model as _Model, rest_discriminator, rest_field +from .._utils.utils import FileType from ._enums import ( AgentBlueprintReferenceType, AgentEndpointAuthorizationSchemeType, @@ -21,11 +22,15 @@ ContainerSkillType, CredentialType, CustomToolParamFormatType, + DataGenerationJobOutputType, + DataGenerationJobSourceType, + DataGenerationJobType, DatasetType, DeploymentType, EvaluationRuleActionType, EvaluationTaxonomyInputType, EvaluatorDefinitionType, + EvaluatorGenerationJobSourceType, FunctionShellToolParamEnvironmentType, IndexType, InsightType, @@ -36,6 +41,9 @@ OpenApiAuthType, PendingUploadType, RecurrenceType, + RoutineActionType, + RoutineDispatchPayloadType, + RoutineTriggerType, SampleType, ScheduleTaskType, TelemetryEndpointAuthType, @@ -59,16 +67,18 @@ class Tool(_Model): A2APreviewTool, ApplyPatchToolParam, AzureAISearchTool, AzureFunctionTool, BingCustomSearchPreviewTool, BingGroundingTool, BrowserAutomationPreviewTool, CaptureStructuredOutputsTool, CodeInterpreterTool, ComputerUsePreviewTool, CustomToolParam, - MicrosoftFabricPreviewTool, FileSearchTool, FunctionTool, ImageGenTool, LocalShellToolParam, - MCPTool, MemorySearchPreviewTool, OpenApiTool, SharepointPreviewTool, FunctionShellToolParam, - WebSearchTool, WebSearchPreviewTool, WorkIQPreviewTool + 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", "azure_ai_search", "azure_function", - "bing_grounding", "capture_structured_outputs", and "openapi". + "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 """ @@ -79,8 +89,8 @@ class Tool(_Model): \"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\", - \"azure_ai_search\", \"azure_function\", \"bing_grounding\", \"capture_structured_outputs\", - and \"openapi\".""" + \"fabric_iq_preview\", \"toolbox_search_preview\", \"azure_ai_search\", \"azure_function\", + \"bing_grounding\", \"capture_structured_outputs\", and \"openapi\".""" @overload def __init__( @@ -105,6 +115,14 @@ class A2APreviewTool(Tool, discriminator="a2a_preview"): :ivar type: The type of the tool. Always ``"a2a_preview``. Required. A2A_PREVIEW. :vartype type: str or ~azure.ai.projects.models.A2A_PREVIEW + :ivar name: Optional user-defined name for this tool or configuration. + :vartype name: str + :ivar description: Optional user-defined description for this tool or configuration. + :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] :ivar base_url: Base URL of the agent. :vartype base_url: str :ivar agent_card_path: The path to the agent card relative to the ``base_url``. If not @@ -118,6 +136,16 @@ class A2APreviewTool(Tool, discriminator="a2a_preview"): type: Literal[ToolType.A2A_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """The type of the tool. Always ``\"a2a_preview``. Required. A2A_PREVIEW.""" + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined name for this tool or configuration.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" base_url: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Base URL of the agent.""" agent_card_path: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -131,6 +159,9 @@ class A2APreviewTool(Tool, discriminator="a2a_preview"): def __init__( self, *, + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, base_url: Optional[str] = None, agent_card_path: Optional[str] = None, project_connection_id: Optional[str] = None, @@ -410,13 +441,101 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = InsightType.AGENT_CLUSTER_INSIGHT # type: ignore +class DataGenerationJobSource(_Model): + """The base source model for data generation jobs. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + AgentDataGenerationJobSource, FileDataGenerationJobSource, PromptDataGenerationJobSource, + TracesDataGenerationJobSource + + :ivar type: The type of source. Required. Known values are: "prompt", "agent", "traces", and + "file". + :vartype type: str or ~azure.ai.projects.models.DataGenerationJobSourceType + :ivar description: Optional description of what this source represents — helps the pipeline + interpret its content (e.g., 'Company refund policy document' or 'Describes the agent's core + capabilities'). + :vartype description: str + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """The type of source. Required. Known values are: \"prompt\", \"agent\", \"traces\", and + \"file\".""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional description of what this source represents — helps the pipeline interpret its content + (e.g., 'Company refund policy document' or 'Describes the agent's core capabilities').""" + + @overload + def __init__( + self, + *, + type: str, + description: 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 AgentDataGenerationJobSource(DataGenerationJobSource, discriminator="agent"): + """Agent source for data generation jobs — references an agent to fetch instructions and metadata + from. + + :ivar description: Optional description of what this source represents — helps the pipeline + interpret its content (e.g., 'Company refund policy document' or 'Describes the agent's core + capabilities'). + :vartype description: str + :ivar type: The source type for this source, which is Agent. Required. Agent source — + references an agent. + :vartype type: str or ~azure.ai.projects.models.AGENT + :ivar agent_name: The agent name to fetch instructions from. Required. + :vartype agent_name: str + :ivar agent_version: The agent version. If not specified, the latest version is used. + :vartype agent_version: str + """ + + type: Literal[DataGenerationJobSourceType.AGENT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The source type for this source, which is Agent. Required. Agent source — references an agent.""" + agent_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The agent name to fetch instructions from. Required.""" + agent_version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The agent version. If not specified, the latest version is used.""" + + @overload + def __init__( + self, + *, + agent_name: str, + description: Optional[str] = None, + 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) + self.type = DataGenerationJobSourceType.AGENT # type: ignore + + class AgentDefinition(_Model): """AgentDefinition. You probably want to use the sub-classes and not this class directly. Known sub-classes are: - HostedAgentDefinition, PromptAgentDefinition, WorkflowAgentDefinition + ExternalAgentDefinition, HostedAgentDefinition, PromptAgentDefinition, WorkflowAgentDefinition - :ivar kind: Required. Known values are: "prompt", "hosted", and "workflow". + :ivar kind: Required. Known values are: "prompt", "hosted", "workflow", and "external". :vartype kind: str or ~azure.ai.projects.models.AgentKind :ivar rai_config: Configuration for Responsible AI (RAI) content filtering and safety features. :vartype rai_config: ~azure.ai.projects.models.RaiConfig @@ -424,7 +543,7 @@ class AgentDefinition(_Model): __mapping__: dict[str, _Model] = {} kind: str = rest_discriminator(name="kind", visibility=["read", "create", "update", "delete", "query"]) - """Required. Known values are: \"prompt\", \"hosted\", and \"workflow\".""" + """Required. Known values are: \"prompt\", \"hosted\", \"workflow\", and \"external\".""" rai_config: Optional["_models.RaiConfig"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Configuration for Responsible AI (RAI) content filtering and safety features.""" @@ -459,7 +578,7 @@ class AgentDetails(_Model): :ivar versions: The latest version of the agent. Required. :vartype versions: ~azure.ai.projects.models.AgentObjectVersions :ivar agent_endpoint: The endpoint configuration for the agent. - :vartype agent_endpoint: ~azure.ai.projects.models.AgentEndpoint + :vartype agent_endpoint: ~azure.ai.projects.models.AgentEndpointConfig :ivar instance_identity: The instance identity of the agent. :vartype instance_identity: ~azure.ai.projects.models.AgentIdentity :ivar blueprint: The blueprint for the agent. @@ -478,7 +597,7 @@ class AgentDetails(_Model): """The name of the agent. Required.""" versions: "_models.AgentObjectVersions" = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The latest version of the agent. Required.""" - agent_endpoint: Optional["_models.AgentEndpoint"] = rest_field( + agent_endpoint: Optional["_models.AgentEndpointConfig"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) """The endpoint configuration for the agent.""" @@ -498,7 +617,7 @@ def __init__( id: str, # pylint: disable=redefined-builtin name: str, versions: "_models.AgentObjectVersions", - agent_endpoint: Optional["_models.AgentEndpoint"] = None, + agent_endpoint: Optional["_models.AgentEndpointConfig"] = None, agent_card: Optional["_models.AgentCard"] = None, ) -> None: ... @@ -513,8 +632,40 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class AgentEndpoint(_Model): - """AgentEndpoint. +class AgentEndpointAuthorizationScheme(_Model): + """AgentEndpointAuthorizationScheme. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + BotServiceAuthorizationScheme, BotServiceRbacAuthorizationScheme, EntraAuthorizationScheme + + :ivar type: Required. Known values are: "Entra", "BotService", and "BotServiceRbac". + :vartype type: str or ~azure.ai.projects.models.AgentEndpointAuthorizationSchemeType + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """Required. Known values are: \"Entra\", \"BotService\", and \"BotServiceRbac\".""" + + @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 AgentEndpointConfig(_Model): + """AgentEndpointConfig. :ivar version_selector: The version selector of the agent endpoint determines how traffic is routed to different versions of the agent. @@ -560,19 +711,22 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class AgentEndpointAuthorizationScheme(_Model): - """AgentEndpointAuthorizationScheme. +class EvaluatorGenerationJobSource(_Model): + """The base source model for evaluator generation jobs. Polymorphic over ``type``. You probably want to use the sub-classes and not this class directly. Known sub-classes are: - BotServiceAuthorizationScheme, BotServiceRbacAuthorizationScheme, EntraAuthorizationScheme + AgentEvaluatorGenerationJobSource, DatasetEvaluatorGenerationJobSource, + PromptEvaluatorGenerationJobSource, TracesEvaluatorGenerationJobSource - :ivar type: Required. Known values are: "Entra", "BotService", and "BotServiceRbac". - :vartype type: str or ~azure.ai.projects.models.AgentEndpointAuthorizationSchemeType + :ivar type: The type of source. Required. Known values are: "prompt", "agent", "traces", and + "dataset". + :vartype type: str or ~azure.ai.projects.models.EvaluatorGenerationJobSourceType """ __mapping__: dict[str, _Model] = {} type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) - """Required. Known values are: \"Entra\", \"BotService\", and \"BotServiceRbac\".""" + """The type of source. Required. Known values are: \"prompt\", \"agent\", \"traces\", and + \"dataset\".""" @overload def __init__( @@ -592,6 +746,55 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class AgentEvaluatorGenerationJobSource(EvaluatorGenerationJobSource, discriminator="agent"): + """Agent source for evaluator generation jobs — references an agent to fetch instructions and + metadata from. + + :ivar description: Optional description of what this source represents — helps the pipeline + interpret its content (e.g., 'Company refund policy document' or 'Describes the agent's core + capabilities'). + :vartype description: str + :ivar type: The source type for this source, which is Agent. Required. Agent source — + references an agent to fetch instructions and metadata from. + :vartype type: str or ~azure.ai.projects.models.AGENT + :ivar agent_name: The agent name to fetch instructions from. Required. + :vartype agent_name: str + :ivar agent_version: The agent version. If not specified, the latest version is used. + :vartype agent_version: str + """ + + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional description of what this source represents — helps the pipeline interpret its content + (e.g., 'Company refund policy document' or 'Describes the agent's core capabilities').""" + type: Literal[EvaluatorGenerationJobSourceType.AGENT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The source type for this source, which is Agent. Required. Agent source — references an agent + to fetch instructions and metadata from.""" + agent_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The agent name to fetch instructions from. Required.""" + agent_version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The agent version. If not specified, the latest version is used.""" + + @overload + def __init__( + self, + *, + agent_name: str, + description: Optional[str] = None, + 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) + self.type = EvaluatorGenerationJobSourceType.AGENT # type: ignore + + class BaseCredentials(_Model): """A base class for connection credentials. @@ -654,6 +857,40 @@ 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. @@ -776,6 +1013,51 @@ 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. @@ -815,14 +1097,14 @@ class AgentTaxonomyInput(EvaluationTaxonomyInput, discriminator="agent"): :ivar type: Input type of the evaluation taxonomy. Required. Agent. :vartype type: str or ~azure.ai.projects.models.AGENT :ivar target: Target configuration for the agent. Required. - :vartype target: ~azure.ai.projects.models.Target + :vartype target: ~azure.ai.projects.models.EvaluationTarget :ivar risk_categories: List of risk categories to evaluate against. Required. :vartype risk_categories: list[str or ~azure.ai.projects.models.RiskCategory] """ type: Literal[EvaluationTaxonomyInputType.AGENT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """Input type of the evaluation taxonomy. Required. Agent.""" - target: "_models.Target" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + target: "_models.EvaluationTarget" = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Target configuration for the agent. Required.""" risk_categories: list[Union[str, "_models.RiskCategory"]] = rest_field( name="riskCategories", visibility=["read", "create", "update", "delete", "query"] @@ -833,7 +1115,7 @@ class AgentTaxonomyInput(EvaluationTaxonomyInput, discriminator="agent"): def __init__( self, *, - target: "_models.Target", + target: "_models.EvaluationTarget", risk_categories: list[Union[str, "_models.RiskCategory"]], ) -> None: ... @@ -1204,6 +1486,45 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type: Literal["approximate"] = "approximate" +class ArtifactProfile(_Model): + """Artifact profile of the model. + + :ivar category: The category of the artifact profile. Required. Known values are: "DataOnly", + "RuntimeDependent", and "Unknown". + :vartype category: str or ~azure.ai.projects.models.FoundryModelArtifactProfileCategory + :ivar signals: Signals detected in the model artifact. + :vartype signals: list[str or ~azure.ai.projects.models.FoundryModelArtifactProfileSignal] + """ + + category: Union[str, "_models.FoundryModelArtifactProfileCategory"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The category of the artifact profile. Required. Known values are: \"DataOnly\", + \"RuntimeDependent\", and \"Unknown\".""" + signals: Optional[list[Union[str, "_models.FoundryModelArtifactProfileSignal"]]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Signals detected in the model artifact.""" + + @overload + def __init__( + self, + *, + category: Union[str, "_models.FoundryModelArtifactProfileCategory"], + signals: Optional[list[Union[str, "_models.FoundryModelArtifactProfileSignal"]]] = 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 AutoCodeInterpreterToolParam(_Model): """Automatic Code Interpreter Tool Parameters. @@ -1250,7 +1571,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type: Literal["auto"] = "auto" -class Target(_Model): +class EvaluationTarget(_Model): """Base class for targets with discriminator support. You probably want to use the sub-classes and not this class directly. Known sub-classes are: @@ -1282,7 +1603,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class AzureAIAgentTarget(Target, discriminator="azure_ai_agent"): +class AzureAIAgentTarget(EvaluationTarget, discriminator="azure_ai_agent"): """Represents a target specifying an Azure AI agent. :ivar type: The type of target, always ``azure_ai_agent``. Required. Default value is @@ -1295,6 +1616,8 @@ class AzureAIAgentTarget(Target, discriminator="azure_ai_agent"): :ivar tool_descriptions: The parameters used to control the sampling behavior of the agent during text generation. :vartype tool_descriptions: list[~azure.ai.projects.models.ToolDescription] + :ivar tools: + :vartype tools: list[~azure.ai.projects.models.Tool] """ type: Literal["azure_ai_agent"] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -1307,6 +1630,7 @@ class AzureAIAgentTarget(Target, discriminator="azure_ai_agent"): visibility=["read", "create", "update", "delete", "query"] ) """The parameters used to control the sampling behavior of the agent during text generation.""" + tools: Optional[list["_models.Tool"]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) @overload def __init__( @@ -1315,6 +1639,7 @@ def __init__( name: str, version: Optional[str] = None, tool_descriptions: Optional[list["_models.ToolDescription"]] = None, + tools: Optional[list["_models.Tool"]] = None, ) -> None: ... @overload @@ -1329,7 +1654,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = "azure_ai_agent" # type: ignore -class AzureAIModelTarget(Target, discriminator="azure_ai_model"): +class AzureAIModelTarget(EvaluationTarget, discriminator="azure_ai_model"): """Represents a target specifying an Azure AI model for operations requiring model selection. :ivar type: The type of target, always ``azure_ai_model``. Required. Default value is @@ -1487,12 +1812,30 @@ class AzureAISearchTool(Tool, discriminator="azure_ai_search"): :ivar type: The object type, which is always 'azure_ai_search'. Required. AZURE_AI_SEARCH. :vartype type: str or ~azure.ai.projects.models.AZURE_AI_SEARCH + :ivar name: Optional user-defined name for this tool or configuration. + :vartype name: str + :ivar description: Optional user-defined description for this tool or configuration. + :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] :ivar azure_ai_search: The azure ai search index resource. Required. :vartype azure_ai_search: ~azure.ai.projects.models.AzureAISearchToolResource """ type: Literal[ToolType.AZURE_AI_SEARCH] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """The object type, which is always 'azure_ai_search'. Required. AZURE_AI_SEARCH.""" + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined name for this tool or configuration.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" azure_ai_search: "_models.AzureAISearchToolResource" = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -1503,6 +1846,9 @@ def __init__( self, *, azure_ai_search: "_models.AzureAISearchToolResource", + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -1716,6 +2062,10 @@ class AzureFunctionTool(Tool, discriminator="azure_function"): :vartype type: str or ~azure.ai.projects.models.AZURE_FUNCTION :ivar azure_function: The Azure Function Tool definition. Required. :vartype azure_function: ~azure.ai.projects.models.AzureFunctionDefinition + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] """ type: Literal[ToolType.AZURE_FUNCTION] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -1724,12 +2074,19 @@ class AzureFunctionTool(Tool, discriminator="azure_function"): visibility=["read", "create", "update", "delete", "query"] ) """The Azure Function Tool definition. Required.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" @overload def __init__( self, *, azure_function: "_models.AzureFunctionDefinition", + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -1744,7 +2101,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = ToolType.AZURE_FUNCTION # type: ignore -class TargetConfig(_Model): +class RedTeamTargetConfig(_Model): """Abstract class for target configuration. You probably want to use the sub-classes and not this class directly. Known sub-classes are: @@ -1776,7 +2133,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class AzureOpenAIModelConfiguration(TargetConfig, discriminator="AzureOpenAIModel"): +class AzureOpenAIModelConfiguration(RedTeamTargetConfig, discriminator="AzureOpenAIModel"): """Azure OpenAI model configuration. The API version would be selected by the service for querying the model. @@ -1876,6 +2233,14 @@ class BingCustomSearchPreviewTool(Tool, discriminator="bing_custom_search_previe :ivar type: The object type, which is always 'bing_custom_search_preview'. Required. BING_CUSTOM_SEARCH_PREVIEW. :vartype type: str or ~azure.ai.projects.models.BING_CUSTOM_SEARCH_PREVIEW + :ivar name: Optional user-defined name for this tool or configuration. + :vartype name: str + :ivar description: Optional user-defined description for this tool or configuration. + :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] :ivar bing_custom_search_preview: The bing custom search tool parameters. Required. :vartype bing_custom_search_preview: ~azure.ai.projects.models.BingCustomSearchToolParameters """ @@ -1883,6 +2248,16 @@ class BingCustomSearchPreviewTool(Tool, discriminator="bing_custom_search_previe type: Literal[ToolType.BING_CUSTOM_SEARCH_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """The object type, which is always 'bing_custom_search_preview'. Required. BING_CUSTOM_SEARCH_PREVIEW.""" + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined name for this tool or configuration.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" bing_custom_search_preview: "_models.BingCustomSearchToolParameters" = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -1893,6 +2268,9 @@ def __init__( self, *, bing_custom_search_preview: "_models.BingCustomSearchToolParameters", + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -2028,12 +2406,30 @@ class BingGroundingTool(Tool, discriminator="bing_grounding"): :ivar type: The object type, which is always 'bing_grounding'. Required. BING_GROUNDING. :vartype type: str or ~azure.ai.projects.models.BING_GROUNDING + :ivar name: Optional user-defined name for this tool or configuration. + :vartype name: str + :ivar description: Optional user-defined description for this tool or configuration. + :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] :ivar bing_grounding: The bing grounding search tool parameters. Required. :vartype bing_grounding: ~azure.ai.projects.models.BingGroundingSearchToolParameters """ type: Literal[ToolType.BING_GROUNDING] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """The object type, which is always 'bing_grounding'. Required. BING_GROUNDING.""" + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined name for this tool or configuration.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" bing_grounding: "_models.BingGroundingSearchToolParameters" = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -2044,6 +2440,9 @@ def __init__( self, *, bing_grounding: "_models.BingGroundingSearchToolParameters", + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -2181,6 +2580,14 @@ class BrowserAutomationPreviewTool(Tool, discriminator="browser_automation_previ :ivar type: The object type, which is always 'browser_automation_preview'. Required. BROWSER_AUTOMATION_PREVIEW. :vartype type: str or ~azure.ai.projects.models.BROWSER_AUTOMATION_PREVIEW + :ivar name: Optional user-defined name for this tool or configuration. + :vartype name: str + :ivar description: Optional user-defined description for this tool or configuration. + :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] :ivar browser_automation_preview: The Browser Automation Tool parameters. Required. :vartype browser_automation_preview: ~azure.ai.projects.models.BrowserAutomationToolParameters """ @@ -2188,6 +2595,16 @@ class BrowserAutomationPreviewTool(Tool, discriminator="browser_automation_previ type: Literal[ToolType.BROWSER_AUTOMATION_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """The object type, which is always 'browser_automation_preview'. Required. BROWSER_AUTOMATION_PREVIEW.""" + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined name for this tool or configuration.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" browser_automation_preview: "_models.BrowserAutomationToolParameters" = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -2198,7 +2615,10 @@ def __init__( self, *, browser_automation_preview: "_models.BrowserAutomationToolParameters", - ) -> None: ... + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, + ) -> None: ... @overload def __init__(self, mapping: Mapping[str, Any]) -> None: @@ -2272,6 +2692,208 @@ 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. @@ -2282,6 +2904,10 @@ class CaptureStructuredOutputsTool(Tool, discriminator="capture_structured_outpu :vartype name: str :ivar description: Optional user-defined description for this tool or configuration. :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] :ivar outputs: The structured outputs to capture from the model. Required. :vartype outputs: ~azure.ai.projects.models.StructuredOutputDefinition """ @@ -2293,6 +2919,12 @@ class CaptureStructuredOutputsTool(Tool, discriminator="capture_structured_outpu """Optional user-defined name for this tool or configuration.""" description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" outputs: "_models.StructuredOutputDefinition" = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -2305,6 +2937,7 @@ def __init__( outputs: "_models.StructuredOutputDefinition", name: Optional[str] = None, description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -2361,7 +2994,7 @@ class MemoryItem(_Model): """A single memory item stored in the memory store, containing content and metadata. You probably want to use the sub-classes and not this class directly. Known sub-classes are: - ChatSummaryMemoryItem, UserProfileMemoryItem + ChatSummaryMemoryItem, ProceduralMemoryItem, UserProfileMemoryItem :ivar memory_id: The unique ID of the memory item. Required. :vartype memory_id: str @@ -2372,8 +3005,8 @@ class MemoryItem(_Model): :vartype scope: str :ivar content: The content of the memory. Required. :vartype content: str - :ivar kind: The kind of the memory item. Required. Known values are: "user_profile" and - "chat_summary". + :ivar kind: The kind of the memory item. Required. Known values are: "user_profile", + "chat_summary", and "procedural". :vartype kind: str or ~azure.ai.projects.models.MemoryItemKind """ @@ -2389,7 +3022,8 @@ class MemoryItem(_Model): content: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The content of the memory. Required.""" kind: str = rest_discriminator(name="kind", visibility=["read", "create", "update", "delete", "query"]) - """The kind of the memory item. Required. Known values are: \"user_profile\" and \"chat_summary\".""" + """The kind of the memory item. Required. Known values are: \"user_profile\", \"chat_summary\", + and \"procedural\".""" @overload def __init__( @@ -2579,10 +3213,10 @@ class EvaluatorDefinition(_Model): """Base evaluator configuration with discriminator. You probably want to use the sub-classes and not this class directly. Known sub-classes are: - CodeBasedEvaluatorDefinition, PromptBasedEvaluatorDefinition + CodeBasedEvaluatorDefinition, PromptBasedEvaluatorDefinition, RubricBasedEvaluatorDefinition :ivar type: The type of evaluator definition. Required. Known values are: "prompt", "code", - "prompt_and_code", "service", and "openai_graders". + "prompt_and_code", "service", "openai_graders", and "rubric". :vartype type: str or ~azure.ai.projects.models.EvaluatorDefinitionType :ivar init_parameters: The JSON schema (Draft 2020-12) for the evaluator's input parameters. This includes parameters like type, properties, required. @@ -2597,7 +3231,7 @@ class EvaluatorDefinition(_Model): __mapping__: dict[str, _Model] = {} type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) """The type of evaluator definition. Required. Known values are: \"prompt\", \"code\", - \"prompt_and_code\", \"service\", and \"openai_graders\".""" + \"prompt_and_code\", \"service\", \"openai_graders\", and \"rubric\".""" init_parameters: Optional[dict[str, Any]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The JSON schema (Draft 2020-12) for the evaluator's input parameters. This includes parameters like type, properties, required.""" @@ -2699,6 +3333,16 @@ class CodeConfiguration(_Model): :vartype runtime: str :ivar entry_point: The entry point command and arguments for the code execution. Required. :vartype entry_point: list[str] + :ivar dependency_resolution: How package dependencies are resolved at deployment time. Defaults + to ``bundled``, where the caller bundles all dependencies into the uploaded zip and the service + performs no remote build. ``remote_build`` instructs the service to build dependencies remotely + from the manifest included in the uploaded zip. Required. Known values are: "bundled" and + "remote_build". + :vartype dependency_resolution: str or ~azure.ai.projects.models.CodeDependencyResolution + :ivar content_hash: The SHA-256 hex digest of the uploaded code zip. Set by the service from + the ``x-ms-code-zip-sha256`` request header; read-only in responses and never accepted in + request payloads. + :vartype content_hash: str """ runtime: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -2706,6 +3350,17 @@ class CodeConfiguration(_Model): Required.""" entry_point: list[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The entry point command and arguments for the code execution. Required.""" + dependency_resolution: Union[str, "_models.CodeDependencyResolution"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """How package dependencies are resolved at deployment time. Defaults to ``bundled``, where the + caller bundles all dependencies into the uploaded zip and the service performs no remote build. + ``remote_build`` instructs the service to build dependencies remotely from the manifest + included in the uploaded zip. Required. Known values are: \"bundled\" and \"remote_build\".""" + content_hash: Optional[str] = rest_field(visibility=["read"]) + """The SHA-256 hex digest of the uploaded code zip. Set by the service from the + ``x-ms-code-zip-sha256`` request header; read-only in responses and never accepted in request + payloads.""" @overload def __init__( @@ -2713,6 +3368,7 @@ def __init__( *, runtime: str, entry_point: list[str], + dependency_resolution: Union[str, "_models.CodeDependencyResolution"], ) -> None: ... @overload @@ -2736,6 +3392,10 @@ class CodeInterpreterTool(Tool, discriminator="code_interpreter"): :vartype name: str :ivar description: Optional user-defined description for this tool or configuration. :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] :ivar container: The code interpreter container. Can be a container ID or an object that specifies uploaded file IDs to make available to your code, along with an optional ``memory_limit`` setting. If not provided, the service assumes auto. Is either a str type or a @@ -2749,6 +3409,12 @@ class CodeInterpreterTool(Tool, discriminator="code_interpreter"): """Optional user-defined name for this tool or configuration.""" description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" container: Optional[Union[str, "_models.AutoCodeInterpreterToolParam"]] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -2763,6 +3429,7 @@ def __init__( *, name: Optional[str] = None, description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, container: Optional[Union[str, "_models.AutoCodeInterpreterToolParam"]] = None, ) -> None: ... @@ -3398,6 +4065,172 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = IndexType.COSMOS_DB # type: ignore +class CreateAgentVersionFromCodeContent(_Model): + """Multipart request body for updating or versioning a code-based agent (POST /agents/{name} and + POST /agents/{name}/versions). + + :ivar metadata: JSON metadata including description and hosted definition. Required. + :vartype metadata: ~azure.ai.projects.models.CreateAgentVersionFromCodeMetadata + :ivar code: The code zip file (max 250 MB). Required. + :vartype code: ~azure.ai.projects._utils.utils.FileType + """ + + metadata: "_models.CreateAgentVersionFromCodeMetadata" = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """JSON metadata including description and hosted definition. Required.""" + code: FileType = rest_field( + visibility=["read", "create", "update", "delete", "query"], is_multipart_file_input=True + ) + """The code zip file (max 250 MB). Required.""" + + @overload + def __init__( + self, + *, + metadata: "_models.CreateAgentVersionFromCodeMetadata", + code: FileType, + ) -> 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 CreateAgentVersionFromCodeMetadata(_Model): + """JSON metadata for code-based agent operations (create, update, create version). The agent name + comes from the URL path parameter or the ``x-ms-agent-name`` header, so it is not included in + this model. The content hash (SHA-256 of the zip) is carried in the ``x-ms-code-zip-sha256`` + header. + + :ivar description: A human-readable description of the agent. + :vartype description: str + :ivar metadata: Set of 16 key-value pairs that can be attached to an object. This can be + useful for storing additional information about the object in a structured + format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings + with a maximum length of 512 characters. + :vartype metadata: dict[str, str] + :ivar definition: The hosted agent definition including code_configuration (runtime, + entry_point), cpu, memory, and protocol_versions. Required. + :vartype definition: ~azure.ai.projects.models.HostedAgentDefinition + """ + + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A human-readable description of the agent.""" + metadata: Optional[dict[str, str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Set of 16 key-value pairs that can be attached to an object. This can be + useful for storing additional information about the object in a structured + format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings + with a maximum length of 512 characters.""" + definition: "_models.HostedAgentDefinition" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The hosted agent definition including code_configuration (runtime, entry_point), cpu, memory, + and protocol_versions. Required.""" + + @overload + def __init__( + self, + *, + definition: "_models.HostedAgentDefinition", + description: Optional[str] = None, + metadata: Optional[dict[str, 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 CreateAsyncResponse(_Model): + """CreateAsyncResponse. + + :ivar location: URL to poll for operation status. + :vartype location: str + :ivar operation_result: URL to the operation result, or null if the operation is still in + progress. + :vartype operation_result: str + """ + + location: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """URL to poll for operation status.""" + operation_result: Optional[str] = rest_field( + name="operationResult", visibility=["read", "create", "update", "delete", "query"] + ) + """URL to the operation result, or null if the operation is still in progress.""" + + @overload + def __init__( + self, + *, + location: Optional[str] = None, + operation_result: 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 CreateSkillVersionFromFilesBody(_Model): + """Multipart request body for creating a skill version from files. Accepts either a single zip + file or multiple individual skill files (directory upload). For zip uploads, the server + extracts and validates contents. For directory uploads, files are validated as-is. + + :ivar files: Skill files to upload. Upload a single zip file or multiple individual files with + relative paths. Required. + :vartype files: list[~azure.ai.projects._utils.utils.FileType] + :ivar default: Whether to set this version as the default. Defaults to false. + :vartype default: bool + """ + + files: list[FileType] = rest_field( + visibility=["read", "create", "update", "delete", "query"], is_multipart_file_input=True + ) + """Skill files to upload. Upload a single zip file or multiple individual files with relative + paths. Required.""" + default: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether to set this version as the default. Defaults to false.""" + + @overload + def __init__( + self, + *, + files: list[FileType], + default: 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) + + class Trigger(_Model): """Base model for Trigger of the schedule. @@ -3438,7 +4271,7 @@ class CronTrigger(Trigger, discriminator="Cron"): :vartype type: str or ~azure.ai.projects.models.CRON :ivar expression: Cron expression that defines the schedule frequency. Required. :vartype expression: str - :ivar time_zone: Time zone for the cron schedule. + :ivar time_zone: Time zone for the cron schedule. Defaults to ``UTC``. :vartype time_zone: str :ivar start_time: Start time for the cron schedule in ISO 8601 format. :vartype start_time: ~datetime.datetime @@ -3451,7 +4284,7 @@ class CronTrigger(Trigger, discriminator="Cron"): expression: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Cron expression that defines the schedule frequency. Required.""" time_zone: Optional[str] = rest_field(name="timeZone", visibility=["read", "create", "update", "delete", "query"]) - """Time zone for the cron schedule.""" + """Time zone for the cron schedule. Defaults to ``UTC``.""" start_time: Optional[datetime.datetime] = rest_field( name="startTime", visibility=["read", "create", "update", "delete", "query"], format="rfc3339" ) @@ -3584,19 +4417,27 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = CustomToolParamFormatType.GRAMMAR # type: ignore -class CustomTextFormatParam(CustomToolParamFormat, discriminator="text"): - """Text format. +class RoutineTrigger(_Model): + """Base model for a routine trigger. - :ivar type: Unconstrained text format. Always ``text``. Required. TEXT. - :vartype type: str or ~azure.ai.projects.models.TEXT + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + CustomRoutineTrigger, GitHubIssueRoutineTrigger, ScheduleRoutineTrigger, TimerRoutineTrigger + + :ivar type: The trigger type. Required. Known values are: "custom", "github_issue", "schedule", + and "timer". + :vartype type: str or ~azure.ai.projects.models.RoutineTriggerType """ - type: Literal[CustomToolParamFormatType.TEXT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore - """Unconstrained text format. Always ``text``. Required. TEXT.""" + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """The trigger type. Required. Known values are: \"custom\", \"github_issue\", \"schedule\", and + \"timer\".""" @overload def __init__( self, + *, + type: str, ) -> None: ... @overload @@ -3608,20 +4449,89 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.type = CustomToolParamFormatType.TEXT # type: ignore -class CustomToolParam(Tool, discriminator="custom"): - """Custom tool. +class CustomRoutineTrigger(RoutineTrigger, discriminator="custom"): + """A custom event routine trigger. - :ivar type: The type of the custom tool. Always ``custom``. Required. CUSTOM. + :ivar type: The trigger type. Required. A custom event trigger. :vartype type: str or ~azure.ai.projects.models.CUSTOM - :ivar name: The name of the custom tool, used to identify it in tool calls. Required. - :vartype name: str - :ivar description: Optional description of the custom tool, used to provide more context. - :vartype description: str - :ivar format: The input format for the custom tool. Default is unconstrained text. - :vartype format: ~azure.ai.projects.models.CustomToolParamFormat + :ivar provider: The external provider that emits the custom event. Required. + :vartype provider: str + :ivar event_name: The provider-specific event name that fires the routine. + :vartype event_name: str + :ivar parameters: Provider-specific trigger parameters. Required. + :vartype parameters: dict[str, any] + """ + + type: Literal[RoutineTriggerType.CUSTOM] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The trigger type. Required. A custom event trigger.""" + provider: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The external provider that emits the custom event. Required.""" + event_name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The provider-specific event name that fires the routine.""" + parameters: dict[str, Any] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Provider-specific trigger parameters. Required.""" + + @overload + def __init__( + self, + *, + provider: str, + parameters: dict[str, Any], + event_name: 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) + self.type = RoutineTriggerType.CUSTOM # type: ignore + + +class CustomTextFormatParam(CustomToolParamFormat, discriminator="text"): + """Text format. + + :ivar type: Unconstrained text format. Always ``text``. Required. TEXT. + :vartype type: str or ~azure.ai.projects.models.TEXT + """ + + type: Literal[CustomToolParamFormatType.TEXT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Unconstrained text format. Always ``text``. Required. TEXT.""" + + @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 = CustomToolParamFormatType.TEXT # type: ignore + + +class CustomToolParam(Tool, discriminator="custom"): + """Custom tool. + + :ivar type: The type of the custom tool. Always ``custom``. Required. CUSTOM. + :vartype type: str or ~azure.ai.projects.models.CUSTOM + :ivar name: The name of the custom tool, used to identify it in tool calls. Required. + :vartype name: str + :ivar description: Optional description of the custom tool, used to provide more context. + :vartype description: str + :ivar format: The input format for the custom tool. Default is unconstrained text. + :vartype format: ~azure.ai.projects.models.CustomToolParamFormat """ type: Literal[ToolType.CUSTOM] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -3724,23 +4634,53 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = RecurrenceType.DAILY # type: ignore -class DatasetCredential(_Model): - """Represents a reference to a blob for consumption. +class DataGenerationJob(_Model): + """Data Generation Job resource. - :ivar blob_reference: Credential info to access the storage account. Required. - :vartype blob_reference: ~azure.ai.projects.models.BlobReference + :ivar id: Server-assigned unique identifier. Required. + :vartype id: str + :ivar inputs: Caller-supplied inputs. + :vartype inputs: ~azure.ai.projects.models.DataGenerationJobInputs + :ivar result: Result produced on success. + :vartype result: ~azure.ai.projects.models.DataGenerationJobResult + :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 (seconds + since January 1, 1970). Required. + :vartype created_at: ~datetime.datetime + :ivar finished_at: The timestamp when the job was finished, represented in Unix time (seconds + since January 1, 1970). + :vartype finished_at: ~datetime.datetime """ - blob_reference: "_models.BlobReference" = rest_field( - name="blobReference", visibility=["read", "create", "update", "delete", "query"] + id: str = rest_field(visibility=["read"]) + """Server-assigned unique identifier. Required.""" + inputs: Optional["_models.DataGenerationJobInputs"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] ) - """Credential info to access the storage account. Required.""" + """Caller-supplied inputs.""" + result: Optional["_models.DataGenerationJobResult"] = 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.""" + created_at: datetime.datetime = rest_field(visibility=["read"], format="unix-timestamp") + """The timestamp when the job was created, represented in Unix time (seconds since January 1, + 1970). Required.""" + finished_at: Optional[datetime.datetime] = rest_field(visibility=["read"], format="unix-timestamp") + """The timestamp when the job was finished, represented in Unix time (seconds since January 1, + 1970).""" @overload def __init__( self, *, - blob_reference: "_models.BlobReference", + inputs: Optional["_models.DataGenerationJobInputs"] = None, ) -> None: ... @overload @@ -3754,67 +4694,53 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class DatasetVersion(_Model): - """DatasetVersion Definition. - - You probably want to use the sub-classes and not this class directly. Known sub-classes are: - FileDatasetVersion, FolderDatasetVersion +class DataGenerationJobInputs(_Model): + """Caller-supplied inputs for a data generation job. - :ivar data_uri: URI of the data (`example `_). - Required. - :vartype data_uri: str - :ivar type: Dataset type. Required. Known values are: "uri_file" and "uri_folder". - :vartype type: str or ~azure.ai.projects.models.DatasetType - :ivar is_reference: Indicates if the dataset holds a reference to the storage, or the dataset - manages storage itself. If true, the underlying data will not be deleted when the dataset - version is deleted. - :vartype is_reference: bool - :ivar connection_name: The Azure Storage Account connection name. Required if - startPendingUploadVersion was not called before creating the Dataset. - :vartype connection_name: str - :ivar id: Asset ID, a unique identifier for the asset. - :vartype id: str - :ivar name: The name of the resource. Required. + :ivar name: The display name of the data generation job. Required. :vartype name: str - :ivar version: The version of the resource. Required. - :vartype version: str - :ivar description: The asset description text. - :vartype description: str - :ivar tags: Tag dictionary. Tags can be added, removed, and updated. - :vartype tags: dict[str, str] + :ivar sources: The sources used for the data generation job. Required. + :vartype sources: list[~azure.ai.projects.models.DataGenerationJobSource] + :ivar options: The options for the data generation job. Required. + :vartype options: ~azure.ai.projects.models.DataGenerationJobOptions + :ivar scenario: The scenario of the data generation job. Either for fine-tuning or evaluation. + Required. Known values are: "supervised_finetuning", "reinforcement_finetuning", and + "evaluation". + :vartype scenario: str or ~azure.ai.projects.models.DataGenerationJobScenario + :ivar output_options: Optional caller-supplied metadata for the job's output. See individual + fields for whether they apply to file outputs (fine-tuning scenarios), dataset outputs + (evaluation scenario), or both. + :vartype output_options: ~azure.ai.projects.models.DataGenerationJobOutputOptions """ - __mapping__: dict[str, _Model] = {} - data_uri: str = rest_field(name="dataUri", visibility=["read", "create"]) - """URI of the data (`example `_). Required.""" - type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) - """Dataset type. Required. Known values are: \"uri_file\" and \"uri_folder\".""" - is_reference: Optional[bool] = rest_field(name="isReference", visibility=["read"]) - """Indicates if the dataset holds a reference to the storage, or the dataset manages storage - itself. If true, the underlying data will not be deleted when the dataset version is deleted.""" - connection_name: Optional[str] = rest_field(name="connectionName", visibility=["read", "create"]) - """The Azure Storage Account connection name. Required if startPendingUploadVersion was not called - before creating the Dataset.""" - id: Optional[str] = rest_field(visibility=["read"]) - """Asset ID, a unique identifier for the asset.""" - name: str = rest_field(visibility=["read"]) - """The name of the resource. Required.""" - version: str = rest_field(visibility=["read"]) - """The version of the resource. Required.""" - description: Optional[str] = rest_field(visibility=["create", "update"]) - """The asset description text.""" - tags: Optional[dict[str, str]] = rest_field(visibility=["create", "update"]) - """Tag dictionary. Tags can be added, removed, and updated.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The display name of the data generation job. Required.""" + sources: list["_models.DataGenerationJobSource"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The sources used for the data generation job. Required.""" + options: "_models.DataGenerationJobOptions" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The options for the data generation job. Required.""" + scenario: Union[str, "_models.DataGenerationJobScenario"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The scenario of the data generation job. Either for fine-tuning or evaluation. Required. Known + values are: \"supervised_finetuning\", \"reinforcement_finetuning\", and \"evaluation\".""" + output_options: Optional["_models.DataGenerationJobOutputOptions"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Optional caller-supplied metadata for the job's output. See individual fields for whether they + apply to file outputs (fine-tuning scenarios), dataset outputs (evaluation scenario), or both.""" @overload def __init__( self, *, - data_uri: str, - type: str, - connection_name: Optional[str] = None, - description: Optional[str] = None, - tags: Optional[dict[str, str]] = None, + name: str, + sources: list["_models.DataGenerationJobSource"], + options: "_models.DataGenerationJobOptions", + scenario: Union[str, "_models.DataGenerationJobScenario"], + output_options: Optional["_models.DataGenerationJobOutputOptions"] = None, ) -> None: ... @overload @@ -3828,33 +4754,47 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class DeleteAgentResponse(_Model): - """A deleted agent Object. +class DataGenerationJobOptions(_Model): + """Options for managing data generation jobs. - :ivar object: The object type. Always 'agent.deleted'. Required. AGENT_DELETED. - :vartype object: str or ~azure.ai.projects.models.AGENT_DELETED - :ivar name: The name of the agent. Required. - :vartype name: str - :ivar deleted: Whether the agent was successfully deleted. Required. - :vartype deleted: bool + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + SimpleQnADataGenerationJobOptions, ToolUseFineTuningDataGenerationJobOptions, + TracesDataGenerationJobOptions + + :ivar type: The data generation job type. Required. Known values are: "simple_qna", "traces", + and "tool_use". + :vartype type: str or ~azure.ai.projects.models.DataGenerationJobType + :ivar max_samples: Maximum number of samples to generate. Required. + :vartype max_samples: int + :ivar train_split: The proportion of the generated data to be used for training when the data + is used for fine-tuning. The rest will be used for validation. Value should be between 0 and 1. + :vartype train_split: float + :ivar model_options: The LLM model options. + :vartype model_options: ~azure.ai.projects.models.DataGenerationModelOptions """ - object: Literal[AgentObjectType.AGENT_DELETED] = rest_field( + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """The data generation job type. Required. Known values are: \"simple_qna\", \"traces\", and + \"tool_use\".""" + max_samples: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Maximum number of samples to generate. Required.""" + train_split: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The proportion of the generated data to be used for training when the data is used for + fine-tuning. The rest will be used for validation. Value should be between 0 and 1.""" + model_options: Optional["_models.DataGenerationModelOptions"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """The object type. Always 'agent.deleted'. Required. AGENT_DELETED.""" - name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The name of the agent. Required.""" - deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Whether the agent was successfully deleted. Required.""" + """The LLM model options.""" @overload def __init__( self, *, - object: Literal[AgentObjectType.AGENT_DELETED], - name: str, - deleted: bool, + type: str, + max_samples: int, + train_split: Optional[float] = None, + model_options: Optional["_models.DataGenerationModelOptions"] = None, ) -> None: ... @overload @@ -3868,38 +4808,25 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class DeleteAgentVersionResponse(_Model): - """A deleted agent version Object. +class DataGenerationJobOutput(_Model): + """Output information for a data generation job. - :ivar object: The object type. Always 'agent.version.deleted'. Required. AGENT_VERSION_DELETED. - :vartype object: str or ~azure.ai.projects.models.AGENT_VERSION_DELETED - :ivar name: The name of the agent. Required. - :vartype name: str - :ivar version: The version identifier of the agent. Required. - :vartype version: str - :ivar deleted: Whether the agent was successfully deleted. Required. - :vartype deleted: bool + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + DatasetDataGenerationJobOutput, FileDataGenerationJobOutput + + :ivar type: The type of the output. Required. Known values are: "file" and "dataset". + :vartype type: str or ~azure.ai.projects.models.DataGenerationJobOutputType """ - object: Literal[AgentObjectType.AGENT_VERSION_DELETED] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """The object type. Always 'agent.version.deleted'. Required. AGENT_VERSION_DELETED.""" - name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The name of the agent. Required.""" - version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The version identifier of the agent. Required.""" - deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Whether the agent was successfully deleted. Required.""" + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """The type of the output. Required. Known values are: \"file\" and \"dataset\".""" @overload def __init__( self, *, - object: Literal[AgentObjectType.AGENT_VERSION_DELETED], - name: str, - version: str, - deleted: bool, + type: str, ) -> None: ... @overload @@ -3913,33 +4840,37 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class DeleteMemoryStoreResult(_Model): - """DeleteMemoryStoreResult. +class DataGenerationJobOutputOptions(_Model): + """Output options for data generation job. - :ivar object: The object type. Always 'memory_store.deleted'. Required. MEMORY_STORE_DELETED. - :vartype object: str or ~azure.ai.projects.models.MEMORY_STORE_DELETED - :ivar name: The name of the memory store. Required. + :ivar name: Name to assign to the output. Used as the filename for Azure OpenAI file outputs + (fine-tuning scenarios) and as the dataset name for dataset outputs (evaluation scenario). :vartype name: str - :ivar deleted: Whether the memory store was successfully deleted. Required. - :vartype deleted: bool + :ivar description: Description to assign to the output. Applies only to dataset outputs + (evaluation scenario); ignored for Azure OpenAI file outputs. + :vartype description: str + :ivar tags: Tags to assign to the output. Applies only to dataset outputs (evaluation + scenario); ignored for Azure OpenAI file outputs. + :vartype tags: dict[str, str] """ - object: Literal[MemoryStoreObjectType.MEMORY_STORE_DELETED] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """The object type. Always 'memory_store.deleted'. Required. MEMORY_STORE_DELETED.""" - name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The name of the memory store. Required.""" - deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Whether the memory store was successfully deleted. Required.""" + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Name to assign to the output. Used as the filename for Azure OpenAI file outputs (fine-tuning + scenarios) and as the dataset name for dataset outputs (evaluation scenario).""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Description to assign to the output. Applies only to dataset outputs (evaluation scenario); + ignored for Azure OpenAI file outputs.""" + tags: Optional[dict[str, str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Tags to assign to the output. Applies only to dataset outputs (evaluation scenario); ignored + for Azure OpenAI file outputs.""" @overload def __init__( self, *, - object: Literal[MemoryStoreObjectType.MEMORY_STORE_DELETED], - name: str, - deleted: bool, + name: Optional[str] = None, + description: Optional[str] = None, + tags: Optional[dict[str, str]] = None, ) -> None: ... @overload @@ -3953,26 +4884,36 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class DeleteSkillResponse(_Model): - """A deleted skill Object. +class DataGenerationJobResult(_Model): + """Result produced by a successful data generation job. - :ivar name: The unique name of the skill. Required. - :vartype name: str - :ivar deleted: Whether the skill was successfully deleted. Required. - :vartype deleted: bool + :ivar outputs: The final job outputs: Azure OpenAI files for fine-tuning, or datasets for + evaluation. + :vartype outputs: list[~azure.ai.projects.models.DataGenerationJobOutput] + :ivar generated_samples: The number of samples actually generated. Required. + :vartype generated_samples: int + :ivar token_usage: The token usage information for the data generation job. + :vartype token_usage: ~azure.ai.projects.models.DataGenerationTokenUsage """ - name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The unique name of the skill. Required.""" - deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Whether the skill was successfully deleted. Required.""" + outputs: Optional[list["_models.DataGenerationJobOutput"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The final job outputs: Azure OpenAI files for fine-tuning, or datasets for evaluation.""" + generated_samples: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The number of samples actually generated. Required.""" + token_usage: Optional["_models.DataGenerationTokenUsage"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The token usage information for the data generation job.""" @overload def __init__( self, *, - name: str, - deleted: bool, + generated_samples: int, + outputs: Optional[list["_models.DataGenerationJobOutput"]] = None, + token_usage: Optional["_models.DataGenerationTokenUsage"] = None, ) -> None: ... @overload @@ -3986,29 +4927,21 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class Deployment(_Model): - """Model Deployment Definition. - - You probably want to use the sub-classes and not this class directly. Known sub-classes are: - ModelDeployment +class DataGenerationModelOptions(_Model): + """LLM model options for data generation jobs. - :ivar type: The type of the deployment. Required. "ModelDeployment" - :vartype type: str or ~azure.ai.projects.models.DeploymentType - :ivar name: Name of the deployment. Required. - :vartype name: str + :ivar model: Base model name used to generate data. Required. + :vartype model: str """ - __mapping__: dict[str, _Model] = {} - type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) - """The type of the deployment. Required. \"ModelDeployment\"""" - name: str = rest_field(visibility=["read"]) - """Name of the deployment. Required.""" + model: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Base model name used to generate data. Required.""" @overload def __init__( self, *, - type: str, + model: str, ) -> None: ... @overload @@ -4022,21 +4955,730 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class EmbeddingConfiguration(_Model): - """Embedding configuration class. +class DataGenerationTokenUsage(_Model): + """Token usage information for a data generation job. - :ivar model_deployment_name: Deployment name of embedding model. It can point to a model - deployment either in the parent AIServices or a connection. Required. - :vartype model_deployment_name: str - :ivar embedding_field: Embedding field. Required. - :vartype embedding_field: str + :ivar prompt_tokens: The number of prompt tokens used. Required. + :vartype prompt_tokens: int + :ivar completion_tokens: The number of completion tokens generated. Required. + :vartype completion_tokens: int + :ivar total_tokens: Total number of tokens used. Required. + :vartype total_tokens: int """ - model_deployment_name: str = rest_field(name="modelDeploymentName", visibility=["create"]) - """Deployment name of embedding model. It can point to a model deployment either in the parent - AIServices or a connection. Required.""" - embedding_field: str = rest_field(name="embeddingField", visibility=["create"]) - """Embedding field. Required.""" + prompt_tokens: int = rest_field(visibility=["read"]) + """The number of prompt tokens used. Required.""" + completion_tokens: int = rest_field(visibility=["read"]) + """The number of completion tokens generated. Required.""" + total_tokens: int = rest_field(visibility=["read"]) + """Total number of tokens used. Required.""" + + +class DatasetCredential(_Model): + """Represents a reference to a blob for consumption. + + :ivar blob_reference: Credential info to access the storage account. Required. + :vartype blob_reference: ~azure.ai.projects.models.BlobReference + """ + + blob_reference: "_models.BlobReference" = rest_field( + name="blobReference", visibility=["read", "create", "update", "delete", "query"] + ) + """Credential info to access the storage account. Required.""" + + @overload + def __init__( + self, + *, + blob_reference: "_models.BlobReference", + ) -> 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 DatasetDataGenerationJobOutput(DataGenerationJobOutput, discriminator="dataset"): + """Dataset output for a data generation job. + + :ivar type: Dataset output. Required. The generated data is a Dataset. + :vartype type: str or ~azure.ai.projects.models.DATASET + :ivar id: The id of the output dataset created. + :vartype id: str + :ivar name: The name of the output dataset. + :vartype name: str + :ivar version: The version of the output dataset. + :vartype version: str + :ivar description: Description of the output dataset. + :vartype description: str + :ivar tags: Tag dictionary of the output dataset. + :vartype tags: dict[str, str] + """ + + type: Literal[DataGenerationJobOutputType.DATASET] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Dataset output. Required. The generated data is a Dataset.""" + id: Optional[str] = rest_field(visibility=["read"]) + """The id of the output dataset created.""" + name: Optional[str] = rest_field(visibility=["read"]) + """The name of the output dataset.""" + version: Optional[str] = rest_field(visibility=["read"]) + """The version of the output dataset.""" + description: Optional[str] = rest_field(visibility=["read"]) + """Description of the output dataset.""" + tags: Optional[dict[str, str]] = rest_field(visibility=["read"]) + """Tag dictionary of the output dataset.""" + + @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 = DataGenerationJobOutputType.DATASET # type: ignore + + +class DatasetEvaluatorGenerationJobSource(EvaluatorGenerationJobSource, discriminator="dataset"): + """Dataset source for evaluator generation jobs — reference to a dataset. + + :ivar description: Optional description of what this source represents — helps the pipeline + interpret its content (e.g., 'Company refund policy document' or 'Describes the agent's core + capabilities'). + :vartype description: str + :ivar type: The source type for this source, which is Dataset. Required. Dataset source — + reference to a dataset. + :vartype type: str or ~azure.ai.projects.models.DATASET + :ivar name: The name of the dataset. Required. + :vartype name: str + :ivar version: The version of the dataset. If not specified, the latest version is used. + :vartype version: str + """ + + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional description of what this source represents — helps the pipeline interpret its content + (e.g., 'Company refund policy document' or 'Describes the agent's core capabilities').""" + type: Literal[EvaluatorGenerationJobSourceType.DATASET] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The source type for this source, which is Dataset. Required. Dataset source — reference to a + dataset.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the dataset. Required.""" + version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The version of the dataset. If not specified, the latest version is used.""" + + @overload + def __init__( + self, + *, + name: str, + description: 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) + 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. + + :ivar name: Dataset name. Required. + :vartype name: str + :ivar version: Dataset version. Required. + :vartype version: str + """ + + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Dataset name. Required.""" + version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Dataset version. Required.""" + + @overload + def __init__( + self, + *, + name: str, + 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 DatasetVersion(_Model): + """DatasetVersion Definition. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + FileDatasetVersion, FolderDatasetVersion + + :ivar data_uri: URI of the data (`example `_). + Required. + :vartype data_uri: str + :ivar type: Dataset type. Required. Known values are: "uri_file" and "uri_folder". + :vartype type: str or ~azure.ai.projects.models.DatasetType + :ivar is_reference: Indicates if the dataset holds a reference to the storage, or the dataset + manages storage itself. If true, the underlying data will not be deleted when the dataset + version is deleted. + :vartype is_reference: bool + :ivar connection_name: The Azure Storage Account connection name. Required if + startPendingUploadVersion was not called before creating the Dataset. + :vartype connection_name: str + :ivar id: Asset ID, a unique identifier for the asset. + :vartype id: str + :ivar name: The name of the resource. Required. + :vartype name: str + :ivar version: The version of the resource. Required. + :vartype version: str + :ivar description: The asset description text. + :vartype description: str + :ivar tags: Tag dictionary. Tags can be added, removed, and updated. + :vartype tags: dict[str, str] + """ + + __mapping__: dict[str, _Model] = {} + data_uri: str = rest_field(name="dataUri", visibility=["read", "create"]) + """URI of the data (`example `_). Required.""" + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """Dataset type. Required. Known values are: \"uri_file\" and \"uri_folder\".""" + is_reference: Optional[bool] = rest_field(name="isReference", visibility=["read"]) + """Indicates if the dataset holds a reference to the storage, or the dataset manages storage + itself. If true, the underlying data will not be deleted when the dataset version is deleted.""" + connection_name: Optional[str] = rest_field(name="connectionName", visibility=["read", "create"]) + """The Azure Storage Account connection name. Required if startPendingUploadVersion was not called + before creating the Dataset.""" + id: Optional[str] = rest_field(visibility=["read"]) + """Asset ID, a unique identifier for the asset.""" + name: str = rest_field(visibility=["read"]) + """The name of the resource. Required.""" + version: str = rest_field(visibility=["read"]) + """The version of the resource. Required.""" + description: Optional[str] = rest_field(visibility=["create", "update"]) + """The asset description text.""" + tags: Optional[dict[str, str]] = rest_field(visibility=["create", "update"]) + """Tag dictionary. Tags can be added, removed, and updated.""" + + @overload + def __init__( + self, + *, + data_uri: str, + type: str, + connection_name: Optional[str] = None, + description: Optional[str] = None, + tags: Optional[dict[str, 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 DeleteAgentResponse(_Model): + """A deleted agent Object. + + :ivar object: The object type. Always 'agent.deleted'. Required. AGENT_DELETED. + :vartype object: str or ~azure.ai.projects.models.AGENT_DELETED + :ivar name: The name of the agent. Required. + :vartype name: str + :ivar deleted: Whether the agent was successfully deleted. Required. + :vartype deleted: bool + """ + + object: Literal[AgentObjectType.AGENT_DELETED] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The object type. Always 'agent.deleted'. Required. AGENT_DELETED.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the agent. Required.""" + deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the agent was successfully deleted. Required.""" + + @overload + def __init__( + self, + *, + object: Literal[AgentObjectType.AGENT_DELETED], + name: str, + deleted: bool, + ) -> 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 DeleteAgentVersionResponse(_Model): + """A deleted agent version Object. + + :ivar object: The object type. Always 'agent.version.deleted'. Required. AGENT_VERSION_DELETED. + :vartype object: str or ~azure.ai.projects.models.AGENT_VERSION_DELETED + :ivar name: The name of the agent. Required. + :vartype name: str + :ivar version: The version identifier of the agent. Required. + :vartype version: str + :ivar deleted: Whether the agent was successfully deleted. Required. + :vartype deleted: bool + """ + + object: Literal[AgentObjectType.AGENT_VERSION_DELETED] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The object type. Always 'agent.version.deleted'. Required. AGENT_VERSION_DELETED.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the agent. Required.""" + version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The version identifier of the agent. Required.""" + deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the agent was successfully deleted. Required.""" + + @overload + def __init__( + self, + *, + object: Literal[AgentObjectType.AGENT_VERSION_DELETED], + name: str, + version: str, + deleted: bool, + ) -> 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 DeleteMemoryResult(_Model): + """Response for deleting a memory item from a memory store. + + :ivar object: The object type. Always 'memory_store.item.deleted'. Required. MEMORY_DELETED. + :vartype object: str or ~azure.ai.projects.models.MEMORY_DELETED + :ivar memory_id: The unique ID of the deleted memory item. Required. + :vartype memory_id: str + :ivar deleted: Whether the memory item was successfully deleted. Required. + :vartype deleted: bool + """ + + object: Literal[MemoryStoreObjectType.MEMORY_DELETED] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The object type. Always 'memory_store.item.deleted'. Required. MEMORY_DELETED.""" + memory_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique ID of the deleted memory item. Required.""" + deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the memory item was successfully deleted. Required.""" + + @overload + def __init__( + self, + *, + object: Literal[MemoryStoreObjectType.MEMORY_DELETED], + memory_id: str, + deleted: bool, + ) -> 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 DeleteMemoryStoreResult(_Model): + """DeleteMemoryStoreResult. + + :ivar object: The object type. Always 'memory_store.deleted'. Required. MEMORY_STORE_DELETED. + :vartype object: str or ~azure.ai.projects.models.MEMORY_STORE_DELETED + :ivar name: The name of the memory store. Required. + :vartype name: str + :ivar deleted: Whether the memory store was successfully deleted. Required. + :vartype deleted: bool + """ + + object: Literal[MemoryStoreObjectType.MEMORY_STORE_DELETED] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The object type. Always 'memory_store.deleted'. Required. MEMORY_STORE_DELETED.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the memory store. Required.""" + deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the memory store was successfully deleted. Required.""" + + @overload + def __init__( + self, + *, + object: Literal[MemoryStoreObjectType.MEMORY_STORE_DELETED], + name: str, + deleted: bool, + ) -> 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 DeleteSkillResult(_Model): + """A deleted skill. + + :ivar id: The unique identifier of the deleted skill. Required. + :vartype id: str + :ivar name: The unique name of the skill. Required. + :vartype name: str + :ivar deleted: Whether the skill was successfully deleted. Required. + :vartype deleted: bool + """ + + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique identifier of the deleted skill. Required.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique name of the skill. Required.""" + deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the skill was successfully deleted. Required.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + name: str, + deleted: bool, + ) -> 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 DeleteSkillVersionResponse(_Model): + """A deleted skill version. + + :ivar id: The unique identifier of the deleted skill version. Required. + :vartype id: str + :ivar name: The name of the skill. Required. + :vartype name: str + :ivar deleted: Whether the skill version was successfully deleted. Required. + :vartype deleted: bool + :ivar version: The version that was deleted. Required. + :vartype version: str + """ + + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique identifier of the deleted skill version. Required.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the skill. Required.""" + deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the skill version was successfully deleted. Required.""" + version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The version that was deleted. Required.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + name: str, + deleted: bool, + 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 Deployment(_Model): + """Model Deployment Definition. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + ModelDeployment + + :ivar type: The type of the deployment. Required. "ModelDeployment" + :vartype type: str or ~azure.ai.projects.models.DeploymentType + :ivar name: Name of the deployment. Required. + :vartype name: str + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """The type of the deployment. Required. \"ModelDeployment\"""" + name: str = rest_field(visibility=["read"]) + """Name of the deployment. Required.""" + + @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 Dimension(_Model): + """A single dimension — one independent, measurable quality dimension within a rubric evaluator's + scoring blueprint. + + :ivar id: Stable identifier for this dimension (snake_case, e.g., ``correct_resolution``). + Required. Provided by the user when manually creating a rubric evaluator or during + human-in-the-loop review of a generated set; the generation pipeline produces an initial value + the user can edit. Editable when saving new versions. Required. + :vartype id: str + :ivar description: What this dimension measures (e.g., 'Correctly identifies the user's + reservation intent and pursues the appropriate workflow'). Required. + :vartype description: str + :ivar weight: Relative weight of this dimension (1-10). The generation pipeline assigns exactly + one dimension weight 8-10; all others use 1-6. User edits are not constrained by this + heuristic. Required. + :vartype weight: int + :ivar always_applicable: When true, the LLM judge always scores this dimension regardless of + relevance (skips applicability assessment). The service-generated general quality/policy + dimension has this set to true and is non-editable. Users may set this on their own custom + dimensions. Defaults to ``false``. + :vartype always_applicable: bool + """ + + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Stable identifier for this dimension (snake_case, e.g., ``correct_resolution``). Required. + Provided by the user when manually creating a rubric evaluator or during human-in-the-loop + review of a generated set; the generation pipeline produces an initial value the user can edit. + Editable when saving new versions. Required.""" + description: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """What this dimension measures (e.g., 'Correctly identifies the user's reservation intent and + pursues the appropriate workflow'). Required.""" + weight: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Relative weight of this dimension (1-10). The generation pipeline assigns exactly one dimension + weight 8-10; all others use 1-6. User edits are not constrained by this heuristic. Required.""" + always_applicable: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """When true, the LLM judge always scores this dimension regardless of relevance (skips + applicability assessment). The service-generated general quality/policy dimension has this set + to true and is non-editable. Users may set this on their own custom dimensions. Defaults to + ``false``.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + description: str, + weight: int, + always_applicable: 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) + + +class DispatchRoutineResult(_Model): + """Identifiers returned after a routine dispatch is queued. + + :ivar dispatch_id: The dispatch identifier created for the routine dispatch. + :vartype dispatch_id: str + :ivar action_correlation_id: A downstream action correlation identifier, when available. + :vartype action_correlation_id: str + :ivar task_id: A workspace task identifier created for the dispatch, when available. + :vartype task_id: str + """ + + dispatch_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The dispatch identifier created for the routine dispatch.""" + action_correlation_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A downstream action correlation identifier, when available.""" + task_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A workspace task identifier created for the dispatch, when available.""" + + @overload + def __init__( + self, + *, + dispatch_id: Optional[str] = None, + action_correlation_id: Optional[str] = None, + task_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 EmbeddingConfiguration(_Model): + """Embedding configuration class. + + :ivar model_deployment_name: Deployment name of embedding model. It can point to a model + deployment either in the parent AIServices or a connection. Required. + :vartype model_deployment_name: str + :ivar embedding_field: Embedding field. Required. + :vartype embedding_field: str + """ + + model_deployment_name: str = rest_field(name="modelDeploymentName", visibility=["create"]) + """Deployment name of embedding model. It can point to a model deployment either in the parent + AIServices or a connection. Required.""" + embedding_field: str = rest_field(name="embeddingField", visibility=["create"]) + """Embedding field. Required.""" @overload def __init__( @@ -4062,22 +5704,24 @@ class EntraAuthorizationScheme(AgentEndpointAuthorizationScheme, discriminator=" :ivar type: Required. ENTRA. :vartype type: str or ~azure.ai.projects.models.ENTRA - :ivar isolation_key_source: Required. + :ivar isolation_key_source: The source from which the per-user isolation key is derived for + requests authorized via this scheme. Defaults to Entra-based isolation when omitted. :vartype isolation_key_source: ~azure.ai.projects.models.IsolationKeySource """ type: Literal[AgentEndpointAuthorizationSchemeType.ENTRA] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """Required. ENTRA.""" - isolation_key_source: "_models.IsolationKeySource" = rest_field( + isolation_key_source: Optional["_models.IsolationKeySource"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """Required.""" + """The source from which the per-user isolation key is derived for requests authorized via this + scheme. Defaults to Entra-based isolation when omitted.""" @overload def __init__( self, *, - isolation_key_source: "_models.IsolationKeySource", + isolation_key_source: Optional["_models.IsolationKeySource"] = None, ) -> None: ... @overload @@ -4873,6 +6517,271 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class EvaluatorCredentialRequest(_Model): + """Request body for getting evaluator credentials. + + :ivar blob_uri: The blob URI for the evaluator storage. Example: + ``https://account.blob.core.windows.net:443/container``. Required. + :vartype blob_uri: str + """ + + blob_uri: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The blob URI for the evaluator storage. Example: + ``https://account.blob.core.windows.net:443/container``. Required.""" + + @overload + def __init__( + self, + *, + blob_uri: 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 EvaluatorGenerationArtifacts(_Model): + """Service-managed provenance artifacts produced by an evaluator generation job. Present only on + EvaluatorVersion resources created via the generation pipeline. The combined-JSONL Foundry + Dataset is read-only and resolves to a versioned dataset in a service-reserved namespace. + + :ivar dataset: Reference to the single Foundry Dataset (one combined JSONL file, + version-aligned to ``EvaluatorVersion.version``) holding all artifacts produced by the + generation pipeline. Each row in the JSONL carries a ``kind`` field discriminating its content + (e.g. ``spec``, ``tools``, ``context``). Required. + :vartype dataset: ~azure.ai.projects.models.DatasetReference + :ivar kinds: The kinds of rows present in ``dataset``. Always contains ``"spec"`` (the + generated evaluation specification, a Markdown document describing what the evaluator + measures). May additionally contain ``"tools"`` (when the generation pipeline produced or + inferred OpenAI tool schemas) and/or ``"context"`` (when supplementary materials such as file + uploads or trace samples were used during generation). Required. + :vartype kinds: list[str] + """ + + dataset: "_models.DatasetReference" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Reference to the single Foundry Dataset (one combined JSONL file, version-aligned to + ``EvaluatorVersion.version``) holding all artifacts produced by the generation pipeline. Each + row in the JSONL carries a ``kind`` field discriminating its content (e.g. ``spec``, ``tools``, + ``context``). Required.""" + kinds: list[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The kinds of rows present in ``dataset``. Always contains ``\"spec\"`` (the generated + evaluation specification, a Markdown document describing what the evaluator measures). May + additionally contain ``\"tools\"`` (when the generation pipeline produced or inferred OpenAI + tool schemas) and/or ``\"context\"`` (when supplementary materials such as file uploads or + trace samples were used during generation). Required.""" + + @overload + def __init__( + self, + *, + dataset: "_models.DatasetReference", + kinds: list[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 EvaluatorGenerationInputs(_Model): + """Caller-supplied inputs for an evaluator generation job. + + :ivar sources: Source materials for generation — agent descriptions, prompts, traces, or + datasets. Each entry is an ``EvaluatorGenerationJobSource`` variant discriminated by ``type``. + Required. + :vartype sources: list[~azure.ai.projects.models.EvaluatorGenerationJobSource] + :ivar model: The LLM model to use for rubric generation (e.g., 'gpt-4o'). Required — users must + provide their own model rather than relying on service-owned capacity. Required. + :vartype model: str + :ivar evaluator_name: The evaluator name (immutable identifier). 1-256 characters; allowed + characters are ASCII letters, digits, underscore (``_``), period (``.``), tilde (``~``), and + hyphen (``-``). The prefix ``builtin.`` is reserved for system-managed evaluators and is + rejected by the service. If an evaluator with this name already exists in the project (and is + rubric-subtype), the service creates a new version under the same name and uses the prior + version's ``dimensions`` as context for incremental improvement (foundation of the post-//build + adaptive loop). Old versions remain queryable via ``get_version(name, version)``. If the + existing evaluator is not a rubric-subtype evaluator (built-in, prompt-based, code-based), the + request is rejected with ``400 Bad Request``. Required. + :vartype evaluator_name: str + :ivar evaluator_display_name: Optional human-friendly display name for the resulting evaluator. + Surfaced as ``EvaluatorVersion.display_name`` on the persisted evaluator. When omitted, the + service uses ``evaluator_name`` as the display name. The ``evaluator_`` prefix disambiguates + this from the immutable ``evaluator_name`` identifier. + :vartype evaluator_display_name: str + :ivar evaluator_description: Optional human-friendly description for the resulting evaluator. + Surfaced as ``EvaluatorVersion.description`` on the persisted evaluator. Typically collected + from the UI alongside ``evaluator_display_name``. The ``evaluator_`` prefix disambiguates this + from any other description fields on related models. + :vartype evaluator_description: str + """ + + sources: list["_models.EvaluatorGenerationJobSource"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Source materials for generation — agent descriptions, prompts, traces, or datasets. Each entry + is an ``EvaluatorGenerationJobSource`` variant discriminated by ``type``. Required.""" + model: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The LLM model to use for rubric generation (e.g., 'gpt-4o'). Required — users must provide + their own model rather than relying on service-owned capacity. Required.""" + evaluator_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The evaluator name (immutable identifier). 1-256 characters; allowed characters are ASCII + letters, digits, underscore (``_``), period (``.``), tilde (``~``), and hyphen (``-``). The + prefix ``builtin.`` is reserved for system-managed evaluators and is rejected by the service. + If an evaluator with this name already exists in the project (and is rubric-subtype), the + service creates a new version under the same name and uses the prior version's ``dimensions`` + as context for incremental improvement (foundation of the post-//build adaptive loop). Old + versions remain queryable via ``get_version(name, version)``. If the existing evaluator is not + a rubric-subtype evaluator (built-in, prompt-based, code-based), the request is rejected with + ``400 Bad Request``. Required.""" + evaluator_display_name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional human-friendly display name for the resulting evaluator. Surfaced as + ``EvaluatorVersion.display_name`` on the persisted evaluator. When omitted, the service uses + ``evaluator_name`` as the display name. The ``evaluator_`` prefix disambiguates this from the + immutable ``evaluator_name`` identifier.""" + evaluator_description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional human-friendly description for the resulting evaluator. Surfaced as + ``EvaluatorVersion.description`` on the persisted evaluator. Typically collected from the UI + alongside ``evaluator_display_name``. The ``evaluator_`` prefix disambiguates this from any + other description fields on related models.""" + + @overload + def __init__( + self, + *, + sources: list["_models.EvaluatorGenerationJobSource"], + model: str, + evaluator_name: str, + evaluator_display_name: Optional[str] = None, + evaluator_description: 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 EvaluatorGenerationJob(_Model): + """Evaluator Generation Job resource — a long-running job that generates rubric-based evaluator + definitions from source materials. On success, the result is the persisted EvaluatorVersion. + + :ivar id: Server-assigned unique identifier. Required. + :vartype id: str + :ivar inputs: Caller-supplied inputs. + :vartype inputs: ~azure.ai.projects.models.EvaluatorGenerationInputs + :ivar result: Result produced on success. + :vartype result: ~azure.ai.projects.models.EvaluatorVersion + :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 (seconds + since January 1, 1970). Required. + :vartype created_at: ~datetime.datetime + :ivar finished_at: The timestamp when the job finished, represented in Unix time (seconds since + January 1, 1970). + :vartype finished_at: ~datetime.datetime + :ivar usage: Token consumption summary. Populated when the job reaches a terminal state. + :vartype usage: ~azure.ai.projects.models.EvaluatorGenerationTokenUsage + """ + + id: str = rest_field(visibility=["read"]) + """Server-assigned unique identifier. Required.""" + inputs: Optional["_models.EvaluatorGenerationInputs"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Caller-supplied inputs.""" + result: Optional["_models.EvaluatorVersion"] = 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.""" + created_at: datetime.datetime = rest_field(visibility=["read"], format="unix-timestamp") + """The timestamp when the job was created, represented in Unix time (seconds since January 1, + 1970). Required.""" + finished_at: Optional[datetime.datetime] = rest_field(visibility=["read"], format="unix-timestamp") + """The timestamp when the job finished, represented in Unix time (seconds since January 1, 1970).""" + usage: Optional["_models.EvaluatorGenerationTokenUsage"] = rest_field(visibility=["read"]) + """Token consumption summary. Populated when the job reaches a terminal state.""" + + @overload + def __init__( + self, + *, + inputs: Optional["_models.EvaluatorGenerationInputs"] = 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 EvaluatorGenerationTokenUsage(_Model): + """Token consumption summary for an evaluator generation job. Populated when the job reaches a + terminal state. + + :ivar input_tokens: Number of input (prompt) tokens consumed. Required. + :vartype input_tokens: int + :ivar output_tokens: Number of output (completion) tokens generated. Required. + :vartype output_tokens: int + :ivar total_tokens: Total tokens consumed (input + output). Required. + :vartype total_tokens: int + """ + + input_tokens: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Number of input (prompt) tokens consumed. Required.""" + output_tokens: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Number of output (completion) tokens generated. Required.""" + total_tokens: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Total tokens consumed (input + output). Required.""" + + @overload + def __init__( + self, + *, + input_tokens: int, + output_tokens: int, + total_tokens: 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 EvaluatorMetric(_Model): """Evaluator Metric. @@ -4947,6 +6856,10 @@ class EvaluatorVersion(_Model): :vartype categories: list[str or ~azure.ai.projects.models.EvaluatorCategory] :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; + present only on evaluator versions created via an EvaluatorGenerationJob. Each artifact + resolves to a versioned Foundry Dataset. + :vartype generation_artifacts: ~azure.ai.projects.models.EvaluatorGenerationArtifacts :ivar created_by: Creator of the evaluator. Required. :vartype created_by: str :ivar created_at: Creation date/time of the evaluator. Required. @@ -4978,6 +6891,10 @@ class EvaluatorVersion(_Model): """The categories of the evaluator. Required.""" definition: "_models.EvaluatorDefinition" = rest_field(visibility=["read", "create"]) """Definition of the evaluator. Required.""" + generation_artifacts: Optional["_models.EvaluatorGenerationArtifacts"] = rest_field(visibility=["read"]) + """Provenance artifacts from the generation pipeline. Read-only; present only on evaluator + versions created via an EvaluatorGenerationJob. Each artifact resolves to a versioned Foundry + Dataset.""" created_by: str = rest_field(visibility=["read"]) """Creator of the evaluator. Required.""" created_at: datetime.datetime = rest_field(visibility=["read"], format="rfc3339") @@ -5019,6 +6936,54 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ExternalAgentDefinition(AgentDefinition, discriminator="external"): + """The external agent definition. Represents a third-party agent hosted outside Foundry (for + example, on GCP or AWS). Registration is metadata-only: Foundry records the agent definition to + light up observability experiences (traces, evaluations) over customer-emitted OpenTelemetry + data. + + :ivar rai_config: Configuration for Responsible AI (RAI) content filtering and safety features. + :vartype rai_config: ~azure.ai.projects.models.RaiConfig + :ivar kind: Required. EXTERNAL. + :vartype kind: str or ~azure.ai.projects.models.EXTERNAL + :ivar otel_agent_id: The OpenTelemetry agent identifier used to attribute customer-emitted + spans to this Foundry agent. Spans must include the attribute ``gen_ai.agent.id = + `` to appear under this registration. Defaults to the top-level agent name when + omitted. Provide an explicit value only for migration scenarios where the running external + agent already emits a stable id that differs from the Foundry agent name. The resolved value is + always echoed on read. + :vartype otel_agent_id: str + """ + + kind: Literal[AgentKind.EXTERNAL] = rest_discriminator(name="kind", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. EXTERNAL.""" + otel_agent_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The OpenTelemetry agent identifier used to attribute customer-emitted spans to this Foundry + agent. Spans must include the attribute ``gen_ai.agent.id = `` to appear under + this registration. Defaults to the top-level agent name when omitted. Provide an explicit value + only for migration scenarios where the running external agent already emits a stable id that + differs from the Foundry agent name. The resolved value is always echoed on read.""" + + @overload + def __init__( + self, + *, + rai_config: Optional["_models.RaiConfig"] = None, + otel_agent_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) + self.kind = AgentKind.EXTERNAL # type: ignore + + class FabricDataAgentToolParameters(_Model): """The fabric data agent tool parameters. @@ -5051,6 +7016,81 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class FabricIQPreviewTool(Tool, discriminator="fabric_iq_preview"): + """A FabricIQ server-side tool. + + :ivar type: The object type, which is always 'fabric_iq_preview'. Required. FABRIC_IQ_PREVIEW. + :vartype type: str or ~azure.ai.projects.models.FABRIC_IQ_PREVIEW + :ivar project_connection_id: The ID of the FabricIQ project connection. Required. + :vartype project_connection_id: str + :ivar server_label: (Optional) The label of the FabricIQ MCP server to connect to. + :vartype server_label: str + :ivar server_url: (Optional) The URL of the FabricIQ MCP server. If not provided, the URL from + the project connection will be used. + :vartype server_url: str + :ivar require_approval: (Optional) Whether the agent requires approval before executing + actions. Default is always. Is either a MCPToolRequireApproval type or a str type. + :vartype require_approval: ~azure.ai.projects.models.MCPToolRequireApproval or str + :ivar name: Optional user-defined name for this tool or configuration. + :vartype name: str + :ivar description: Optional user-defined description for this tool or configuration. + :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] + """ + + type: Literal[ToolType.FABRIC_IQ_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The object type, which is always 'fabric_iq_preview'. Required. FABRIC_IQ_PREVIEW.""" + project_connection_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The ID of the FabricIQ project connection. Required.""" + server_label: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """(Optional) The label of the FabricIQ MCP server to connect to.""" + server_url: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """(Optional) The URL of the FabricIQ MCP server. If not provided, the URL from the project + connection will be used.""" + require_approval: Optional[Union["_models.MCPToolRequireApproval", str]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """(Optional) Whether the agent requires approval before executing actions. Default is always. Is + either a MCPToolRequireApproval type or a str type.""" + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined name for this tool or configuration.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" + + @overload + def __init__( + self, + *, + project_connection_id: str, + server_label: Optional[str] = None, + server_url: Optional[str] = None, + require_approval: Optional[Union["_models.MCPToolRequireApproval", str]] = None, + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = 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.FABRIC_IQ_PREVIEW # type: ignore + + class FieldMapping(_Model): """Field mapping configuration class. @@ -5068,29 +7108,102 @@ class FieldMapping(_Model): :vartype metadata_fields: list[str] """ - content_fields: list[str] = rest_field(name="contentFields", visibility=["create"]) - """List of fields with text content. Required.""" - filepath_field: Optional[str] = rest_field(name="filepathField", visibility=["create"]) - """Path of file to be used as a source of text content.""" - title_field: Optional[str] = rest_field(name="titleField", visibility=["create"]) - """Field containing the title of the document.""" - url_field: Optional[str] = rest_field(name="urlField", visibility=["create"]) - """Field containing the url of the document.""" - vector_fields: Optional[list[str]] = rest_field(name="vectorFields", visibility=["create"]) - """List of fields with vector content.""" - metadata_fields: Optional[list[str]] = rest_field(name="metadataFields", visibility=["create"]) - """List of fields with metadata content.""" + content_fields: list[str] = rest_field(name="contentFields", visibility=["create"]) + """List of fields with text content. Required.""" + filepath_field: Optional[str] = rest_field(name="filepathField", visibility=["create"]) + """Path of file to be used as a source of text content.""" + title_field: Optional[str] = rest_field(name="titleField", visibility=["create"]) + """Field containing the title of the document.""" + url_field: Optional[str] = rest_field(name="urlField", visibility=["create"]) + """Field containing the url of the document.""" + vector_fields: Optional[list[str]] = rest_field(name="vectorFields", visibility=["create"]) + """List of fields with vector content.""" + metadata_fields: Optional[list[str]] = rest_field(name="metadataFields", visibility=["create"]) + """List of fields with metadata content.""" + + @overload + def __init__( + self, + *, + content_fields: list[str], + filepath_field: Optional[str] = None, + title_field: Optional[str] = None, + url_field: Optional[str] = None, + vector_fields: Optional[list[str]] = None, + metadata_fields: Optional[list[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 FileDataGenerationJobOutput(DataGenerationJobOutput, discriminator="file"): + """Azure OpenAI file output for a data generation job. + + :ivar type: Azure OpenAI file output. Required. The generated data is an Azure OpenAI File. + :vartype type: str or ~azure.ai.projects.models.FILE + :ivar id: The id of the output Azure OpenAI file. Required. + :vartype id: str + :ivar filename: The filename of the output Azure OpenAI file. Required. + :vartype filename: str + """ + + type: Literal[DataGenerationJobOutputType.FILE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Azure OpenAI file output. Required. The generated data is an Azure OpenAI File.""" + id: str = rest_field(visibility=["read"]) + """The id of the output Azure OpenAI file. Required.""" + filename: str = rest_field(visibility=["read"]) + """The filename of the output Azure OpenAI file. Required.""" + + @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 = DataGenerationJobOutputType.FILE # type: ignore + + +class FileDataGenerationJobSource(DataGenerationJobSource, discriminator="file"): + """File source for data generation jobs — Azure OpenAI file input. + + :ivar description: Optional description of what this source represents — helps the pipeline + interpret its content (e.g., 'Company refund policy document' or 'Describes the agent's core + capabilities'). + :vartype description: str + :ivar type: The source type for this job, which is File. Required. File source — Azure OpenAI + file. + :vartype type: str or ~azure.ai.projects.models.FILE + :ivar id: Input Azure Open AI file id used for data generation. Required. + :vartype id: str + """ + + type: Literal[DataGenerationJobSourceType.FILE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The source type for this job, which is File. Required. File source — Azure OpenAI file.""" + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Input Azure Open AI file id used for data generation. Required.""" @overload def __init__( self, *, - content_fields: list[str], - filepath_field: Optional[str] = None, - title_field: Optional[str] = None, - url_field: Optional[str] = None, - vector_fields: Optional[list[str]] = None, - metadata_fields: Optional[list[str]] = None, + id: str, # pylint: disable=redefined-builtin + description: Optional[str] = None, ) -> None: ... @overload @@ -5102,6 +7215,7 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) + self.type = DataGenerationJobSourceType.FILE # type: ignore class FileDatasetVersion(DatasetVersion, discriminator="uri_file"): @@ -5175,6 +7289,10 @@ class FileSearchTool(Tool, discriminator="file_search"): :vartype name: str :ivar description: Optional user-defined description for this tool or configuration. :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] """ type: Literal[ToolType.FILE_SEARCH] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -5193,6 +7311,12 @@ class FileSearchTool(Tool, discriminator="file_search"): """Optional user-defined name for this tool or configuration.""" description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" @overload def __init__( @@ -5204,6 +7328,7 @@ def __init__( filters: Optional["_types.Filters"] = None, name: Optional[str] = None, description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -5344,6 +7469,42 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = DatasetType.URI_FOLDER # type: ignore +class FoundryModelWarning(_Model): + """A warning associated with a model. + + :ivar code: The warning code. Known values are: "RuntimeDependentArtifact" and + "UnclassifiedArtifact". + :vartype code: str or ~azure.ai.projects.models.FoundryModelWarningCode + :ivar message: The warning message. + :vartype message: str + """ + + code: Optional[Union[str, "_models.FoundryModelWarningCode"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The warning code. Known values are: \"RuntimeDependentArtifact\" and \"UnclassifiedArtifact\".""" + message: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The warning message.""" + + @overload + def __init__( + self, + *, + code: Optional[Union[str, "_models.FoundryModelWarningCode"]] = None, + message: 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 FunctionShellToolParam(Tool, discriminator="shell"): """Shell tool. @@ -5355,6 +7516,10 @@ class FunctionShellToolParam(Tool, discriminator="shell"): :vartype name: str :ivar description: Optional user-defined description for this tool or configuration. :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] """ type: Literal[ToolType.SHELL] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -5366,6 +7531,12 @@ class FunctionShellToolParam(Tool, discriminator="shell"): """Optional user-defined name for this tool or configuration.""" description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" @overload def __init__( @@ -5374,6 +7545,7 @@ def __init__( environment: Optional["_models.FunctionShellToolParamEnvironment"] = None, name: Optional[str] = None, description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -5508,30 +7680,75 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = ToolType.FUNCTION # type: ignore +class GitHubIssueRoutineTrigger(RoutineTrigger, discriminator="github_issue"): + """A GitHub issue routine trigger. + + :ivar type: The trigger type. Required. A GitHub issue trigger. + :vartype type: str or ~azure.ai.projects.models.GITHUB_ISSUE + :ivar connection_id: The workspace connection identifier that resolves the GitHub configuration + for the trigger. Required. + :vartype connection_id: str + :ivar owner: The GitHub owner or organization that scopes which issues can fire the trigger. + Required. + :vartype owner: str + :ivar repository: The GitHub repository filter that scopes which issues can fire the trigger. + Required. + :vartype repository: str + :ivar issue_event: The GitHub issue event that fires the routine. Required. Known values are: + "opened" and "closed". + :vartype issue_event: str or ~azure.ai.projects.models.GitHubIssueEvent + """ + + type: Literal[RoutineTriggerType.GITHUB_ISSUE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The trigger type. Required. A GitHub issue trigger.""" + connection_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The workspace connection identifier that resolves the GitHub configuration for the trigger. + Required.""" + owner: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The GitHub owner or organization that scopes which issues can fire the trigger. Required.""" + repository: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The GitHub repository filter that scopes which issues can fire the trigger. Required.""" + issue_event: Union[str, "_models.GitHubIssueEvent"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The GitHub issue event that fires the routine. Required. Known values are: \"opened\" and + \"closed\".""" + + @overload + def __init__( + self, + *, + connection_id: str, + owner: str, + repository: str, + issue_event: Union[str, "_models.GitHubIssueEvent"], + ) -> 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 = RoutineTriggerType.GITHUB_ISSUE # type: ignore + + class HeaderIsolationKeySource(IsolationKeySource, discriminator="Header"): """HeaderIsolationKeySource. :ivar kind: Required. HEADER. :vartype kind: str or ~azure.ai.projects.models.HEADER - :ivar user_isolation_key: The user isolation key header value. Required. - :vartype user_isolation_key: str - :ivar chat_isolation_key: The chat isolation key header value. Required. - :vartype chat_isolation_key: str """ kind: Literal[IsolationKeySourceKind.HEADER] = rest_discriminator(name="kind", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """Required. HEADER.""" - user_isolation_key: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The user isolation key header value. Required.""" - chat_isolation_key: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The chat isolation key header value. Required.""" @overload def __init__( self, - *, - user_isolation_key: str, - chat_isolation_key: str, ) -> None: ... @overload @@ -5634,17 +7851,12 @@ class HostedAgentDefinition(AgentDefinition, discriminator="hosted"): :ivar tools: An array of tools the hosted agent's model may call while generating a response. You can specify which tool to use by setting the ``tool_choice`` parameter. :vartype tools: list[~azure.ai.projects.models.Tool] - :ivar container_protocol_versions: The protocols that the agent supports for ingress - communication of the containers. - :vartype container_protocol_versions: list[~azure.ai.projects.models.ProtocolVersionRecord] :ivar cpu: The CPU configuration for the hosted agent. Required. :vartype cpu: str :ivar memory: The memory configuration for the hosted agent. Required. :vartype memory: str :ivar environment_variables: Environment variables to set in the hosted agent container. :vartype environment_variables: dict[str, str] - :ivar image: The image ID for the agent, applicable to image-based hosted agents. - :vartype image: str :ivar container_configuration: Container-based deployment configuration. Provide this for image-based deployments. Mutually exclusive with code_configuration — the service validates that exactly one is set. @@ -5665,10 +7877,6 @@ class HostedAgentDefinition(AgentDefinition, discriminator="hosted"): tools: Optional[list["_models.Tool"]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """An array of tools the hosted agent's model may call while generating a response. You can specify which tool to use by setting the ``tool_choice`` parameter.""" - container_protocol_versions: Optional[list["_models.ProtocolVersionRecord"]] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """The protocols that the agent supports for ingress communication of the containers.""" cpu: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The CPU configuration for the hosted agent. Required.""" memory: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -5677,8 +7885,6 @@ class HostedAgentDefinition(AgentDefinition, discriminator="hosted"): visibility=["read", "create", "update", "delete", "query"] ) """Environment variables to set in the hosted agent container.""" - image: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The image ID for the agent, applicable to image-based hosted agents.""" container_configuration: Optional["_models.ContainerConfiguration"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -5707,9 +7913,7 @@ def __init__( memory: str, rai_config: Optional["_models.RaiConfig"] = None, tools: Optional[list["_models.Tool"]] = None, - container_protocol_versions: Optional[list["_models.ProtocolVersionRecord"]] = None, environment_variables: Optional[dict[str, str]] = None, - image: Optional[str] = None, container_configuration: Optional["_models.ContainerConfiguration"] = None, protocol_versions: Optional[list["_models.ProtocolVersionRecord"]] = None, code_configuration: Optional["_models.CodeConfiguration"] = None, @@ -5866,6 +8070,10 @@ class ImageGenTool(Tool, discriminator="image_generation"): :vartype name: str :ivar description: Optional user-defined description for this tool or configuration. :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] """ type: Literal[ToolType.IMAGE_GENERATION] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -5925,6 +8133,12 @@ class ImageGenTool(Tool, discriminator="image_generation"): """Optional user-defined name for this tool or configuration.""" description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" @overload def __init__( @@ -5945,6 +8159,7 @@ def __init__( action: Optional[Union[str, "_models.ImageGenAction"]] = None, name: Optional[str] = None, description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -6287,8 +8502,254 @@ class InsightsMetadata(_Model): def __init__( self, *, - created_at: datetime.datetime, - completed_at: Optional[datetime.datetime] = None, + created_at: datetime.datetime, + completed_at: Optional[datetime.datetime] = 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 InsightSummary(_Model): + """Summary of the error cluster analysis. + + :ivar sample_count: Total number of samples analyzed. Required. + :vartype sample_count: int + :ivar unique_subcluster_count: Total number of unique subcluster labels. Required. + :vartype unique_subcluster_count: int + :ivar unique_cluster_count: Total number of unique clusters. Required. + :vartype unique_cluster_count: int + :ivar method: Method used for clustering. Required. + :vartype method: str + :ivar usage: Token usage while performing clustering analysis. Required. + :vartype usage: ~azure.ai.projects.models.ClusterTokenUsage + """ + + sample_count: int = rest_field(name="sampleCount", visibility=["read", "create", "update", "delete", "query"]) + """Total number of samples analyzed. Required.""" + unique_subcluster_count: int = rest_field( + name="uniqueSubclusterCount", visibility=["read", "create", "update", "delete", "query"] + ) + """Total number of unique subcluster labels. Required.""" + unique_cluster_count: int = rest_field( + name="uniqueClusterCount", visibility=["read", "create", "update", "delete", "query"] + ) + """Total number of unique clusters. Required.""" + method: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Method used for clustering. Required.""" + usage: "_models.ClusterTokenUsage" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Token usage while performing clustering analysis. Required.""" + + @overload + def __init__( + self, + *, + sample_count: int, + unique_subcluster_count: int, + unique_cluster_count: int, + method: str, + usage: "_models.ClusterTokenUsage", + ) -> 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 RoutineDispatchPayload(_Model): + """Base model for a manual dispatch payload. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + InvokeAgentInvocationsApiDispatchPayload, InvokeAgentResponsesApiDispatchPayload + + :ivar type: The manual dispatch payload type. Required. Known values are: + "invoke_agent_responses_api" and "invoke_agent_invocations_api". + :vartype type: str or ~azure.ai.projects.models.RoutineDispatchPayloadType + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """The manual dispatch payload type. Required. Known values are: \"invoke_agent_responses_api\" + and \"invoke_agent_invocations_api\".""" + + @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 InvokeAgentInvocationsApiDispatchPayload(RoutineDispatchPayload, discriminator="invoke_agent_invocations_api"): + """A manual payload used to test an invocations API routine dispatch. + + :ivar type: The manual dispatch payload type. Required. A manual payload for an invocations API + routine dispatch. + :vartype type: str or ~azure.ai.projects.models.INVOKE_AGENT_INVOCATIONS_API + :ivar input: The JSON value sent as the complete downstream invocations input. The value is + passed through as-is and can be an object, string, number, boolean, array, or null. Required. + :vartype input: any + """ + + type: Literal[RoutineDispatchPayloadType.INVOKE_AGENT_INVOCATIONS_API] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The manual dispatch payload type. Required. A manual payload for an invocations API routine + dispatch.""" + input: Any = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The JSON value sent as the complete downstream invocations input. The value is passed through + as-is and can be an object, string, number, boolean, array, or null. Required.""" + + @overload + def __init__( + self, + *, + input: Any, + ) -> 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 = RoutineDispatchPayloadType.INVOKE_AGENT_INVOCATIONS_API # type: ignore + + +class RoutineAction(_Model): + """Base model for a routine action. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + InvokeAgentInvocationsApiRoutineAction, InvokeAgentResponsesApiRoutineAction + + :ivar type: The action type. Required. Known values are: "invoke_agent_responses_api" and + "invoke_agent_invocations_api". + :vartype type: str or ~azure.ai.projects.models.RoutineActionType + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """The action type. Required. Known values are: \"invoke_agent_responses_api\" and + \"invoke_agent_invocations_api\".""" + + @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 InvokeAgentInvocationsApiRoutineAction(RoutineAction, discriminator="invoke_agent_invocations_api"): + """Dispatches a routine through the raw invocations API. Exactly one of agent_name or + agent_endpoint_id must be provided. + + :ivar type: The action type. Required. Dispatches through the raw invocations API. + :vartype type: str or ~azure.ai.projects.models.INVOKE_AGENT_INVOCATIONS_API + :ivar agent_name: The project-scoped agent name for routine dispatch. + :vartype agent_name: str + :ivar agent_endpoint_id: Legacy endpoint-scoped agent identifier for routine dispatch. + :vartype agent_endpoint_id: str + :ivar input: Static JSON value sent as the complete downstream input when the routine fires. + The value is passed through as-is; no templating is applied. + :vartype input: any + :ivar session_id: An optional existing hosted-agent session identifier to continue during the + downstream dispatch. + :vartype session_id: str + """ + + type: Literal[RoutineActionType.INVOKE_AGENT_INVOCATIONS_API] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The action type. Required. Dispatches through the raw invocations API.""" + agent_name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The project-scoped agent name for routine dispatch.""" + agent_endpoint_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Legacy endpoint-scoped agent identifier for routine dispatch.""" + input: Optional[Any] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Static JSON value sent as the complete downstream input when the routine fires. The value is + passed through as-is; no templating is applied.""" + session_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """An optional existing hosted-agent session identifier to continue during the downstream + dispatch.""" + + @overload + def __init__( + self, + *, + agent_name: Optional[str] = None, + agent_endpoint_id: Optional[str] = None, + input: Optional[Any] = None, + session_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) + self.type = RoutineActionType.INVOKE_AGENT_INVOCATIONS_API # type: ignore + + +class InvokeAgentResponsesApiDispatchPayload(RoutineDispatchPayload, discriminator="invoke_agent_responses_api"): + """A manual payload used to test a responses API routine dispatch. + + :ivar type: The manual dispatch payload type. Required. A manual payload for a responses API + routine dispatch. + :vartype type: str or ~azure.ai.projects.models.INVOKE_AGENT_RESPONSES_API + :ivar input: The JSON value sent as the complete downstream responses input. The value is + passed through as-is and can be an object, string, number, boolean, array, or null. Required. + :vartype input: any + """ + + type: Literal[RoutineDispatchPayloadType.INVOKE_AGENT_RESPONSES_API] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The manual dispatch payload type. Required. A manual payload for a responses API routine + dispatch.""" + input: Any = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The JSON value sent as the complete downstream responses input. The value is passed through + as-is and can be an object, string, number, boolean, array, or null. Required.""" + + @overload + def __init__( + self, + *, + input: Any, ) -> None: ... @overload @@ -6300,47 +8761,47 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) + self.type = RoutineDispatchPayloadType.INVOKE_AGENT_RESPONSES_API # type: ignore -class InsightSummary(_Model): - """Summary of the error cluster analysis. +class InvokeAgentResponsesApiRoutineAction(RoutineAction, discriminator="invoke_agent_responses_api"): + """Dispatches a routine through the responses API. Exactly one of agent_name or agent_endpoint_id + must be provided. - :ivar sample_count: Total number of samples analyzed. Required. - :vartype sample_count: int - :ivar unique_subcluster_count: Total number of unique subcluster labels. Required. - :vartype unique_subcluster_count: int - :ivar unique_cluster_count: Total number of unique clusters. Required. - :vartype unique_cluster_count: int - :ivar method: Method used for clustering. Required. - :vartype method: str - :ivar usage: Token usage while performing clustering analysis. Required. - :vartype usage: ~azure.ai.projects.models.ClusterTokenUsage + :ivar type: The action type. Required. Dispatches through the responses API. + :vartype type: str or ~azure.ai.projects.models.INVOKE_AGENT_RESPONSES_API + :ivar agent_name: The project-scoped agent name for routine dispatch. + :vartype agent_name: str + :ivar agent_endpoint_id: Legacy endpoint-scoped agent identifier for routine dispatch. + :vartype agent_endpoint_id: str + :ivar input: Static JSON value sent as the complete downstream input when the routine fires. + The value is passed through as-is; no templating is applied. + :vartype input: any + :ivar conversation: An optional existing conversation identifier to continue during the + downstream dispatch. + :vartype conversation: str """ - sample_count: int = rest_field(name="sampleCount", visibility=["read", "create", "update", "delete", "query"]) - """Total number of samples analyzed. Required.""" - unique_subcluster_count: int = rest_field( - name="uniqueSubclusterCount", visibility=["read", "create", "update", "delete", "query"] - ) - """Total number of unique subcluster labels. Required.""" - unique_cluster_count: int = rest_field( - name="uniqueClusterCount", visibility=["read", "create", "update", "delete", "query"] - ) - """Total number of unique clusters. Required.""" - method: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Method used for clustering. Required.""" - usage: "_models.ClusterTokenUsage" = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Token usage while performing clustering analysis. Required.""" + type: Literal[RoutineActionType.INVOKE_AGENT_RESPONSES_API] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The action type. Required. Dispatches through the responses API.""" + agent_name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The project-scoped agent name for routine dispatch.""" + agent_endpoint_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Legacy endpoint-scoped agent identifier for routine dispatch.""" + input: Optional[Any] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Static JSON value sent as the complete downstream input when the routine fires. The value is + passed through as-is; no templating is applied.""" + conversation: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """An optional existing conversation identifier to continue during the downstream dispatch.""" @overload def __init__( self, *, - sample_count: int, - unique_subcluster_count: int, - unique_cluster_count: int, - method: str, - usage: "_models.ClusterTokenUsage", + agent_name: Optional[str] = None, + agent_endpoint_id: Optional[str] = None, + input: Optional[Any] = None, + conversation: Optional[str] = None, ) -> None: ... @overload @@ -6352,6 +8813,7 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) + self.type = RoutineActionType.INVOKE_AGENT_RESPONSES_API # type: ignore class LocalShellToolParam(Tool, discriminator="local_shell"): @@ -6363,6 +8825,10 @@ class LocalShellToolParam(Tool, discriminator="local_shell"): :vartype name: str :ivar description: Optional user-defined description for this tool or configuration. :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] """ type: Literal[ToolType.LOCAL_SHELL] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -6371,6 +8837,12 @@ class LocalShellToolParam(Tool, discriminator="local_shell"): """Optional user-defined name for this tool or configuration.""" description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" @overload def __init__( @@ -6378,6 +8850,7 @@ def __init__( *, name: Optional[str] = None, description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -6430,6 +8903,54 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class LoraConfig(_Model): + """Adapter-specific metadata for LoRA models. Drives serving engine configuration at deployment + time. + + :ivar rank: LoRA rank (r). Positive integer. Common values: 8, 16, 32, 64. + :vartype rank: int + :ivar alpha: LoRA scaling factor (α). Positive integer; typically 2× the rank. + :vartype alpha: int + :ivar target_modules: Model layers modified by the adapter (e.g., q_proj, v_proj). + Auto-detected from adapter_config.json if omitted. + :vartype target_modules: list[str] + :ivar dropout: Dropout rate used during training. Informational — not used at serving time. + :vartype dropout: float + """ + + rank: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """LoRA rank (r). Positive integer. Common values: 8, 16, 32, 64.""" + alpha: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """LoRA scaling factor (α). Positive integer; typically 2× the rank.""" + target_modules: Optional[list[str]] = rest_field( + name="targetModules", visibility=["read", "create", "update", "delete", "query"] + ) + """Model layers modified by the adapter (e.g., q_proj, v_proj). Auto-detected from + adapter_config.json if omitted.""" + dropout: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Dropout rate used during training. Informational — not used at serving time.""" + + @overload + def __init__( + self, + *, + rank: Optional[int] = None, + alpha: Optional[int] = None, + target_modules: Optional[list[str]] = None, + dropout: Optional[float] = 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 ManagedAgentIdentityBlueprintReference(AgentBlueprintReference, discriminator="ManagedAgentIdentityBlueprint"): """ManagedAgentIdentityBlueprintReference. @@ -6552,6 +9073,10 @@ class MCPTool(Tool, discriminator="mcp"): connection stores authentication and other connection details needed to connect to the MCP server. :vartype project_connection_id: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] """ type: Literal[ToolType.MCP] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -6606,6 +9131,12 @@ class MCPTool(Tool, discriminator="mcp"): 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.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" @overload def __init__( @@ -6631,6 +9162,7 @@ def __init__( allowed_tools: Optional[Union[list[str], "_models.MCPToolFilter"]] = None, require_approval: Optional[Union["_models.MCPToolRequireApproval", Literal["always"], Literal["never"]]] = None, project_connection_id: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -6814,6 +9346,14 @@ class MemorySearchPreviewTool(Tool, discriminator="memory_search_preview"): :ivar type: The type of the tool. Always ``memory_search_preview``. Required. MEMORY_SEARCH_PREVIEW. :vartype type: str or ~azure.ai.projects.models.MEMORY_SEARCH_PREVIEW + :ivar name: Optional user-defined name for this tool or configuration. + :vartype name: str + :ivar description: Optional user-defined description for this tool or configuration. + :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] :ivar memory_store_name: The name of the memory store to use. Required. :vartype memory_store_name: str :ivar scope: The namespace used to group and isolate memories, such as a user ID. Limits which @@ -6829,6 +9369,16 @@ class MemorySearchPreviewTool(Tool, discriminator="memory_search_preview"): type: Literal[ToolType.MEMORY_SEARCH_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """The type of the tool. Always ``memory_search_preview``. Required. MEMORY_SEARCH_PREVIEW.""" + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined name for this tool or configuration.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" memory_store_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The name of the memory store to use. Required.""" scope: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -6848,6 +9398,9 @@ def __init__( *, memory_store_name: str, scope: str, + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, search_options: Optional["_models.MemorySearchOptions"] = None, update_delay: Optional[int] = None, ) -> None: ... @@ -6951,9 +9504,15 @@ class MemoryStoreDefaultOptions(_Model): :ivar user_profile_details: Specific categories or types of user profile information to extract and store. :vartype user_profile_details: str - :ivar chat_summary_enabled: Whether to enable chat summary extraction and storage. Default is - true. Required. + :ivar chat_summary_enabled: Whether to enable chat summary extraction and storage. Defaults to + ``true``. Required. :vartype chat_summary_enabled: bool + :ivar procedural_memory_enabled: Whether to enable procedural memory extraction and storage. + Defaults to ``true``. + :vartype procedural_memory_enabled: bool + :ivar default_ttl_seconds: The default time-to-live for memories in seconds. A value of ``0`` + indicates that memories do not expire. Defaults to ``0``. + :vartype default_ttl_seconds: int """ user_profile_enabled: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -6961,7 +9520,12 @@ class MemoryStoreDefaultOptions(_Model): user_profile_details: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Specific categories or types of user profile information to extract and store.""" chat_summary_enabled: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Whether to enable chat summary extraction and storage. Default is true. Required.""" + """Whether to enable chat summary extraction and storage. Defaults to ``true``. Required.""" + procedural_memory_enabled: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether to enable procedural memory extraction and storage. Defaults to ``true``.""" + default_ttl_seconds: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The default time-to-live for memories in seconds. A value of ``0`` indicates that memories do + not expire. Defaults to ``0``.""" @overload def __init__( @@ -6970,6 +9534,8 @@ def __init__( user_profile_enabled: bool, chat_summary_enabled: bool, user_profile_details: Optional[str] = None, + procedural_memory_enabled: Optional[bool] = None, + default_ttl_seconds: Optional[int] = None, ) -> None: ... @overload @@ -7296,6 +9862,14 @@ class MicrosoftFabricPreviewTool(Tool, discriminator="fabric_dataagent_preview") :ivar type: The object type, which is always 'fabric_dataagent_preview'. Required. FABRIC_DATAAGENT_PREVIEW. :vartype type: str or ~azure.ai.projects.models.FABRIC_DATAAGENT_PREVIEW + :ivar name: Optional user-defined name for this tool or configuration. + :vartype name: str + :ivar description: Optional user-defined description for this tool or configuration. + :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] :ivar fabric_dataagent_preview: The fabric data agent tool parameters. Required. :vartype fabric_dataagent_preview: ~azure.ai.projects.models.FabricDataAgentToolParameters """ @@ -7303,6 +9877,16 @@ class MicrosoftFabricPreviewTool(Tool, discriminator="fabric_dataagent_preview") type: Literal[ToolType.FABRIC_DATAAGENT_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """The object type, which is always 'fabric_dataagent_preview'. Required. FABRIC_DATAAGENT_PREVIEW.""" + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined name for this tool or configuration.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" fabric_dataagent_preview: "_models.FabricDataAgentToolParameters" = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -7313,6 +9897,9 @@ def __init__( self, *, fabric_dataagent_preview: "_models.FabricDataAgentToolParameters", + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -7327,6 +9914,34 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = ToolType.FABRIC_DATAAGENT_PREVIEW # type: ignore +class ModelCredentialRequest(_Model): + """Request to fetch credentials for a model asset. + + :ivar blob_uri: Blob URI of the model asset to fetch credentials for. Required. + :vartype blob_uri: str + """ + + blob_uri: str = rest_field(name="blobUri", visibility=["read", "create", "update", "delete", "query"]) + """Blob URI of the model asset to fetch credentials for. Required.""" + + @overload + def __init__( + self, + *, + blob_uri: 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 ModelDeployment(Deployment, discriminator="ModelDeployment"): """Model Deployment Definition. @@ -7428,37 +10043,261 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ModelPendingUploadRequest(_Model): + """Represents a request for a pending upload of a model version. + + :ivar pending_upload_id: If PendingUploadId is not provided, a random GUID will be used. + :vartype pending_upload_id: str + :ivar connection_name: Azure Storage Account connection name to use for generating temporary + SAS token. + :vartype connection_name: str + :ivar pending_upload_type: The type of pending upload. Only TemporaryBlobReference is supported + for models. Required. Temporary blob reference. + :vartype pending_upload_type: str or ~azure.ai.projects.models.TEMPORARY_BLOB_REFERENCE + """ + + pending_upload_id: Optional[str] = rest_field( + name="pendingUploadId", visibility=["read", "create", "update", "delete", "query"] + ) + """If PendingUploadId is not provided, a random GUID will be used.""" + connection_name: Optional[str] = rest_field( + name="connectionName", visibility=["read", "create", "update", "delete", "query"] + ) + """Azure Storage Account connection name to use for generating temporary SAS token.""" + pending_upload_type: Literal[PendingUploadType.TEMPORARY_BLOB_REFERENCE] = rest_field( + name="pendingUploadType", visibility=["read", "create", "update", "delete", "query"] + ) + """The type of pending upload. Only TemporaryBlobReference is supported for models. Required. + Temporary blob reference.""" + + @overload + def __init__( + self, + *, + pending_upload_type: Literal[PendingUploadType.TEMPORARY_BLOB_REFERENCE], + pending_upload_id: Optional[str] = None, + connection_name: 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 ModelPendingUploadResponse(_Model): + """Represents the response for a model pending upload request. + + :ivar blob_reference: Container-level read, write, list SAS. Required. + :vartype blob_reference: ~azure.ai.projects.models.BlobReference + :ivar pending_upload_id: ID for this upload request. Required. + :vartype pending_upload_id: str + :ivar version: Version of asset to be created if user did not specify version when initially + creating upload. + :vartype version: str + :ivar pending_upload_type: The type of pending upload. Only TemporaryBlobReference is supported + for models. Required. Temporary blob reference. + :vartype pending_upload_type: str or ~azure.ai.projects.models.TEMPORARY_BLOB_REFERENCE + """ + + blob_reference: "_models.BlobReference" = rest_field( + name="blobReference", visibility=["read", "create", "update", "delete", "query"] + ) + """Container-level read, write, list SAS. Required.""" + pending_upload_id: str = rest_field( + name="pendingUploadId", visibility=["read", "create", "update", "delete", "query"] + ) + """ID for this upload request. Required.""" + version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Version of asset to be created if user did not specify version when initially creating upload.""" + pending_upload_type: Literal[PendingUploadType.TEMPORARY_BLOB_REFERENCE] = rest_field( + name="pendingUploadType", visibility=["read", "create", "update", "delete", "query"] + ) + """The type of pending upload. Only TemporaryBlobReference is supported for models. Required. + Temporary blob reference.""" + + @overload + def __init__( + self, + *, + blob_reference: "_models.BlobReference", + pending_upload_id: str, + pending_upload_type: Literal[PendingUploadType.TEMPORARY_BLOB_REFERENCE], + 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 ModelSamplingParams(_Model): """Represents a set of parameters used to control the sampling behavior of a language model during text generation. - :ivar temperature: The temperature parameter for sampling. Required. + :ivar temperature: The temperature parameter for sampling. Defaults to 1.0. :vartype temperature: float - :ivar top_p: The top-p parameter for nucleus sampling. Required. + :ivar top_p: The top-p parameter for nucleus sampling. Defaults to 1.0. :vartype top_p: float - :ivar seed: The random seed for reproducibility. Required. + :ivar seed: The random seed for reproducibility. Defaults to 42. :vartype seed: int - :ivar max_completion_tokens: The maximum number of tokens allowed in the completion. Required. + :ivar max_completion_tokens: The maximum number of tokens allowed in the completion. :vartype max_completion_tokens: int """ - temperature: float = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The temperature parameter for sampling. Required.""" - top_p: float = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The top-p parameter for nucleus sampling. Required.""" - seed: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The random seed for reproducibility. Required.""" - max_completion_tokens: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The maximum number of tokens allowed in the completion. Required.""" + temperature: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The temperature parameter for sampling. Defaults to 1.0.""" + top_p: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The top-p parameter for nucleus sampling. Defaults to 1.0.""" + seed: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The random seed for reproducibility. Defaults to 42.""" + max_completion_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The maximum number of tokens allowed in the completion.""" + + @overload + def __init__( + self, + *, + temperature: Optional[float] = None, + top_p: Optional[float] = None, + seed: Optional[int] = None, + max_completion_tokens: Optional[int] = 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 ModelSourceData(_Model): + """Source information for the model. + + :ivar source_type: The source type of the model. Known values are: "LocalUpload" and + "TrainingJob". + :vartype source_type: str or ~azure.ai.projects.models.FoundryModelSourceType + :ivar job_id: The job ID that produced this model. + :vartype job_id: str + """ + + source_type: Optional[Union[str, "_models.FoundryModelSourceType"]] = rest_field( + name="sourceType", visibility=["read", "create", "update", "delete", "query"] + ) + """The source type of the model. Known values are: \"LocalUpload\" and \"TrainingJob\".""" + job_id: Optional[str] = rest_field(name="jobId", visibility=["read", "create", "update", "delete", "query"]) + """The job ID that produced this model.""" + + @overload + def __init__( + self, + *, + source_type: Optional[Union[str, "_models.FoundryModelSourceType"]] = None, + job_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 ModelVersion(_Model): + """Model Version Definition. + + :ivar system_data: System related metadata. + :vartype system_data: ~azure.ai.projects.models.SystemDataV3 + :ivar blob_uri: URI of the model artifact in blob storage. Required. + :vartype blob_uri: str + :ivar weight_type: The weight type of the model. Known values are: "FullWeight", "LoRA", and + "DraftModel". + :vartype weight_type: str or ~azure.ai.projects.models.FoundryModelWeightType + :ivar base_model: Base model asset ID. + :vartype base_model: str + :ivar source: The source of the model. + :vartype source: ~azure.ai.projects.models.ModelSourceData + :ivar lora_config: Adapter-specific configuration. Required when weight_type is lora; ignored + otherwise. May be auto-populated from adapter_config.json when present in the uploaded files — + user-provided values take precedence over auto-detected values. + :vartype lora_config: ~azure.ai.projects.models.LoraConfig + :ivar artifact_profile: The artifact profile of the model. + :vartype artifact_profile: ~azure.ai.projects.models.ArtifactProfile + :ivar warnings: Service-computed advisory warnings derived from the artifact profile. + :vartype warnings: list[~azure.ai.projects.models.FoundryModelWarning] + :ivar id: Asset ID, a unique identifier for the asset. + :vartype id: str + :ivar name: The name of the resource. Required. + :vartype name: str + :ivar version: The version of the resource. Required. + :vartype version: str + :ivar description: The asset description text. + :vartype description: str + :ivar tags: Tag dictionary. Tags can be added, removed, and updated. + :vartype tags: dict[str, str] + """ + + system_data: Optional["_models.SystemDataV3"] = rest_field(name="systemData", visibility=["read"]) + """System related metadata.""" + blob_uri: str = rest_field(name="blobUri", visibility=["read", "create", "update", "delete", "query"]) + """URI of the model artifact in blob storage. Required.""" + weight_type: Optional[Union[str, "_models.FoundryModelWeightType"]] = rest_field( + name="weightType", visibility=["read", "create", "update", "delete", "query"] + ) + """The weight type of the model. Known values are: \"FullWeight\", \"LoRA\", and \"DraftModel\".""" + base_model: Optional[str] = rest_field(name="baseModel", visibility=["read", "create"]) + """Base model asset ID.""" + source: Optional["_models.ModelSourceData"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The source of the model.""" + lora_config: Optional["_models.LoraConfig"] = rest_field(name="loraConfig", visibility=["read", "create"]) + """Adapter-specific configuration. Required when weight_type is lora; ignored otherwise. May be + auto-populated from adapter_config.json when present in the uploaded files — user-provided + values take precedence over auto-detected values.""" + artifact_profile: Optional["_models.ArtifactProfile"] = rest_field(name="artifactProfile", visibility=["read"]) + """The artifact profile of the model.""" + warnings: Optional[list["_models.FoundryModelWarning"]] = rest_field(visibility=["read"]) + """Service-computed advisory warnings derived from the artifact profile.""" + id: Optional[str] = rest_field(visibility=["read"]) + """Asset ID, a unique identifier for the asset.""" + name: str = rest_field(visibility=["read"]) + """The name of the resource. Required.""" + version: str = rest_field(visibility=["read"]) + """The version of the resource. Required.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update"]) + """The asset description text.""" + tags: Optional[dict[str, str]] = rest_field(visibility=["read", "create", "update"]) + """Tag dictionary. Tags can be added, removed, and updated.""" @overload def __init__( self, *, - temperature: float, - top_p: float, - seed: int, - max_completion_tokens: int, + blob_uri: str, + weight_type: Optional[Union[str, "_models.FoundryModelWeightType"]] = None, + base_model: Optional[str] = None, + source: Optional["_models.ModelSourceData"] = None, + lora_config: Optional["_models.LoraConfig"] = None, + description: Optional[str] = None, + tags: Optional[dict[str, str]] = None, ) -> None: ... @overload @@ -7541,7 +10380,7 @@ class OneTimeTrigger(Trigger, discriminator="OneTime"): :vartype type: str or ~azure.ai.projects.models.ONE_TIME :ivar trigger_at: Date and time for the one-time trigger in ISO 8601 format. Required. :vartype trigger_at: ~datetime.datetime - :ivar time_zone: Time zone for the one-time trigger. + :ivar time_zone: Time zone for the one-time trigger. Defaults to ``UTC``. :vartype time_zone: str """ @@ -7552,7 +10391,7 @@ class OneTimeTrigger(Trigger, discriminator="OneTime"): ) """Date and time for the one-time trigger in ISO 8601 format. Required.""" time_zone: Optional[str] = rest_field(name="timeZone", visibility=["read", "create", "update", "delete", "query"]) - """Time zone for the one-time trigger.""" + """Time zone for the one-time trigger. Defaults to ``UTC``.""" @overload def __init__( @@ -7714,9 +10553,395 @@ class OpenApiFunctionDefinitionFunction(_Model): def __init__( self, *, - name: str, - parameters: dict[str, Any], - description: Optional[str] = None, + name: str, + parameters: dict[str, Any], + description: 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 OpenApiManagedAuthDetails(OpenApiAuthDetails, discriminator="managed_identity"): + """Security details for OpenApi managed_identity authentication. + + :ivar type: The object type, which is always 'managed_identity'. Required. MANAGED_IDENTITY. + :vartype type: str or ~azure.ai.projects.models.MANAGED_IDENTITY + :ivar security_scheme: Connection auth security details. Required. + :vartype security_scheme: ~azure.ai.projects.models.OpenApiManagedSecurityScheme + """ + + type: Literal[OpenApiAuthType.MANAGED_IDENTITY] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The object type, which is always 'managed_identity'. Required. MANAGED_IDENTITY.""" + security_scheme: "_models.OpenApiManagedSecurityScheme" = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Connection auth security details. Required.""" + + @overload + def __init__( + self, + *, + security_scheme: "_models.OpenApiManagedSecurityScheme", + ) -> 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 = OpenApiAuthType.MANAGED_IDENTITY # type: ignore + + +class OpenApiManagedSecurityScheme(_Model): + """Security scheme for OpenApi managed_identity authentication. + + :ivar audience: Authentication scope for managed_identity auth type. Required. + :vartype audience: str + """ + + audience: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Authentication scope for managed_identity auth type. Required.""" + + @overload + def __init__( + self, + *, + audience: 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 OpenApiProjectConnectionAuthDetails(OpenApiAuthDetails, discriminator="project_connection"): + """Security details for OpenApi project connection authentication. + + :ivar type: The object type, which is always 'project_connection'. Required. + PROJECT_CONNECTION. + :vartype type: str or ~azure.ai.projects.models.PROJECT_CONNECTION + :ivar security_scheme: Project connection auth security details. Required. + :vartype security_scheme: ~azure.ai.projects.models.OpenApiProjectConnectionSecurityScheme + """ + + type: Literal[OpenApiAuthType.PROJECT_CONNECTION] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The object type, which is always 'project_connection'. Required. PROJECT_CONNECTION.""" + security_scheme: "_models.OpenApiProjectConnectionSecurityScheme" = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Project connection auth security details. Required.""" + + @overload + def __init__( + self, + *, + security_scheme: "_models.OpenApiProjectConnectionSecurityScheme", + ) -> 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 = OpenApiAuthType.PROJECT_CONNECTION # type: ignore + + +class OpenApiProjectConnectionSecurityScheme(_Model): + """Security scheme for OpenApi managed_identity authentication. + + :ivar project_connection_id: Project connection id for Project Connection auth type. Required. + :vartype project_connection_id: str + """ + + project_connection_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Project connection id for Project Connection auth type. Required.""" + + @overload + def __init__( + self, + *, + project_connection_id: 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 OpenApiTool(Tool, discriminator="openapi"): + """The input definition information for an OpenAPI tool as used to configure an agent. + + :ivar type: The object type, which is always 'openapi'. Required. OPENAPI. + :vartype type: str or ~azure.ai.projects.models.OPENAPI + :ivar openapi: The openapi function definition. Required. + :vartype openapi: ~azure.ai.projects.models.OpenApiFunctionDefinition + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] + """ + + type: Literal[ToolType.OPENAPI] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The object type, which is always 'openapi'. Required. OPENAPI.""" + openapi: "_models.OpenApiFunctionDefinition" = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The openapi function definition. Required.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" + + @overload + def __init__( + self, + *, + openapi: "_models.OpenApiFunctionDefinition", + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = 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.OPENAPI # type: ignore + + +class OptimizationAgentDefinition(_Model): + """Agent definition returned in response payloads (includes resolved config). + + :ivar agent_name: Agent name. + :vartype agent_name: str + :ivar agent_version: Agent version. + :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_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.""" + + @overload + def __init__( + self, + *, + agent_name: Optional[str] = None, + 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 + 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 OptimizationCandidate(_Model): + """Aggregated evaluation result for a single candidate agent configuration across all tasks. + + :ivar candidate_id: Server-assigned candidate identifier. Use with GET /candidates/{id} + sub-endpoints. + :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. + :vartype eval_run_id: str + :ivar promotion: Promotion metadata. Null if the candidate has not been promoted. + :vartype promotion: ~azure.ai.projects.models.PromotionInfo + """ + + candidate_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """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.""" + 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"]) + """Foundry evaluation run identifier for this candidate's scoring run.""" + promotion: Optional["_models.PromotionInfo"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Promotion metadata. Null if the candidate has not been promoted.""" + + @overload + 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, + eval_id: Optional[str] = None, + eval_run_id: Optional[str] = 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 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 + contains scored candidates. + + :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 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. + :vartype updated_at: ~datetime.datetime + :ivar progress: Progress while in flight. Absent in terminal states. + :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 + """ + + 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.""" + 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.""" + 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.""" + + @overload + def __init__( + self, + *, + inputs: Optional["_models.OptimizationJobInputs"] = None, ) -> None: ... @overload @@ -7730,27 +10955,50 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class OpenApiManagedAuthDetails(OpenApiAuthDetails, discriminator="managed_identity"): - """Security details for OpenApi managed_identity authentication. +class OptimizationJobInputs(_Model): + """Caller-supplied inputs for an optimization job. - :ivar type: The object type, which is always 'managed_identity'. Required. MANAGED_IDENTITY. - :vartype type: str or ~azure.ai.projects.models.MANAGED_IDENTITY - :ivar security_scheme: Connection auth security details. Required. - :vartype security_scheme: ~azure.ai.projects.models.OpenApiManagedSecurityScheme + :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] + :ivar options: Tuning knobs and run-mode. + :vartype options: ~azure.ai.projects.models.OptimizationOptions """ - type: Literal[OpenApiAuthType.MANAGED_IDENTITY] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore - """The object type, which is always 'managed_identity'. Required. MANAGED_IDENTITY.""" - security_scheme: "_models.OpenApiManagedSecurityScheme" = rest_field( + agent: "_models.AgentIdentifier" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The agent (and pinned version) being optimized. Required.""" + train_dataset_reference: "_models.DatasetRef" = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """Connection auth security details. Required.""" + """Reference to a registered training dataset (required). Required.""" + validation_dataset_reference: Optional["_models.DatasetRef"] = 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'].""" + options: Optional["_models.OptimizationOptions"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Tuning knobs and run-mode.""" @overload def __init__( self, *, - security_scheme: "_models.OpenApiManagedSecurityScheme", + agent: "_models.AgentIdentifier", + train_dataset_reference: "_models.DatasetRef", + validation_dataset_reference: Optional["_models.DatasetRef"] = None, + evaluators: Optional[list[str]] = None, + options: Optional["_models.OptimizationOptions"] = None, ) -> None: ... @overload @@ -7762,24 +11010,34 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.type = OpenApiAuthType.MANAGED_IDENTITY # type: ignore -class OpenApiManagedSecurityScheme(_Model): - """Security scheme for OpenApi managed_identity authentication. +class OptimizationJobProgress(_Model): + """In-flight progress; only populated while status is queued or in_progress. - :ivar audience: Authentication scope for managed_identity auth type. Required. - :vartype audience: str + :ivar current_iteration: 1-based current iteration index. Required. + :vartype current_iteration: 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. + Required. + :vartype elapsed_seconds: float """ - audience: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Authentication scope for managed_identity auth type. Required.""" + current_iteration: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """1-based current iteration index. 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"]) + """Wall-clock time elapsed in seconds since the job began executing. Required.""" @overload def __init__( self, *, - audience: str, + current_iteration: int, + best_score: float, + elapsed_seconds: float, ) -> None: ... @overload @@ -7793,28 +11051,59 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class OpenApiProjectConnectionAuthDetails(OpenApiAuthDetails, discriminator="project_connection"): - """Security details for OpenApi project connection authentication. +class OptimizationJobResult(_Model): + """Terminal-state result body. Populated when status is succeeded or failed. - :ivar type: The object type, which is always 'project_connection'. Required. - PROJECT_CONNECTION. - :vartype type: str or ~azure.ai.projects.models.PROJECT_CONNECTION - :ivar security_scheme: Project connection auth security details. Required. - :vartype security_scheme: ~azure.ai.projects.models.OpenApiProjectConnectionSecurityScheme + :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 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 """ - type: Literal[OpenApiAuthType.PROJECT_CONNECTION] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore - """The object type, which is always 'project_connection'. Required. PROJECT_CONNECTION.""" - security_scheme: "_models.OpenApiProjectConnectionSecurityScheme" = rest_field( + baseline: Optional["_models.OptimizationCandidate"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """Project connection auth security details. Required.""" + """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.""" + 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, *, - security_scheme: "_models.OpenApiProjectConnectionSecurityScheme", + baseline: Optional["_models.OptimizationCandidate"] = None, + best: Optional["_models.OptimizationCandidate"] = 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 @@ -7826,24 +11115,56 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.type = OpenApiAuthType.PROJECT_CONNECTION # type: ignore -class OpenApiProjectConnectionSecurityScheme(_Model): - """Security scheme for OpenApi managed_identity authentication. +class OptimizationOptions(_Model): + """Tuning knobs and run-mode for an optimization job. - :ivar project_connection_id: Project connection id for Project Connection auth type. Required. - :vartype project_connection_id: str + :ivar max_iterations: Maximum optimization iterations per strategy. Must be >= 1. Default: 5. + :vartype max_iterations: 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] + :ivar eval_model: Model deployment used for evaluation. Defaults to server config (typically + 'gpt-4o'). + :vartype eval_model: str + :ivar optimization_model: Model deployment for optimization reasoning (must be gpt-5 family). + Falls back to the default eval model when not set. + :vartype optimization_model: str + :ivar evaluation_level: Evaluation granularity. Null/omitted means per-item single-turn. Set to + 'conversation' for per-conversation multi-turn simulation scoring. Known values are: "turn" and + "conversation". + :vartype evaluation_level: str or ~azure.ai.projects.models.EvaluationLevel """ - project_connection_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Project connection id for Project Connection auth type. Required.""" + max_iterations: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Maximum optimization iterations per strategy. Must be >= 1. Default: 5.""" + optimization_config: Optional[dict[str, Any]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-target-attribute configuration overrides. Contains skills, tools, system_prompt for the + agent, plus model space for model optimization.""" + eval_model: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Model deployment used for evaluation. Defaults to server config (typically 'gpt-4o').""" + optimization_model: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Model deployment for optimization reasoning (must be gpt-5 family). Falls back to the default + eval model when not set.""" + evaluation_level: Optional[Union[str, "_models.EvaluationLevel"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Evaluation granularity. Null/omitted means per-item single-turn. Set to 'conversation' for + per-conversation multi-turn simulation scoring. Known values are: \"turn\" and + \"conversation\".""" @overload def __init__( self, *, - project_connection_id: str, + max_iterations: Optional[int] = None, + optimization_config: Optional[dict[str, Any]] = None, + eval_model: Optional[str] = None, + optimization_model: Optional[str] = None, + evaluation_level: Optional[Union[str, "_models.EvaluationLevel"]] = None, ) -> None: ... @overload @@ -7857,27 +11178,71 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class OpenApiTool(Tool, discriminator="openapi"): - """The input definition information for an OpenAPI tool as used to configure an agent. +class OptimizationTaskResult(_Model): + """Per-task evaluation result for a single candidate. - :ivar type: The object type, which is always 'openapi'. Required. OPENAPI. - :vartype type: str or ~azure.ai.projects.models.OPENAPI - :ivar openapi: The openapi function definition. Required. - :vartype openapi: ~azure.ai.projects.models.OpenApiFunctionDefinition + :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 """ - type: Literal[ToolType.OPENAPI] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore - """The object type, which is always 'openapi'. Required. OPENAPI.""" - openapi: "_models.OpenApiFunctionDefinition" = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """The openapi function definition. Required.""" + 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.""" @overload def __init__( self, *, - openapi: "_models.OpenApiFunctionDefinition", + 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, ) -> None: ... @overload @@ -7889,7 +11254,6 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.type = ToolType.OPENAPI # type: ignore class TelemetryEndpoint(_Model): @@ -7998,8 +11362,8 @@ class PendingUploadRequest(_Model): :ivar connection_name: Azure Storage Account connection name to use for generating temporary SAS token. :vartype connection_name: str - :ivar pending_upload_type: BlobReference is the only supported type. Required. Blob Reference - is the only supported type. + :ivar pending_upload_type: The type of pending upload. Required. Deprecated: the service never + read this value and silently ignored it. Use TemporaryBlobReference instead. :vartype pending_upload_type: str or ~azure.ai.projects.models.BLOB_REFERENCE """ @@ -8014,7 +11378,8 @@ class PendingUploadRequest(_Model): pending_upload_type: Literal[PendingUploadType.BLOB_REFERENCE] = rest_field( name="pendingUploadType", visibility=["read", "create", "update", "delete", "query"] ) - """BlobReference is the only supported type. Required. Blob Reference is the only supported type.""" + """The type of pending upload. Required. Deprecated: the service never read this value and + silently ignored it. Use TemporaryBlobReference instead.""" @overload def __init__( @@ -8046,8 +11411,8 @@ class PendingUploadResponse(_Model): :ivar version: Version of asset to be created if user did not specify version when initially creating upload. :vartype version: str - :ivar pending_upload_type: BlobReference is the only supported type. Required. Blob Reference - is the only supported type. + :ivar pending_upload_type: The type of pending upload. Required. Deprecated: the service never + read this value and silently ignored it. Use TemporaryBlobReference instead. :vartype pending_upload_type: str or ~azure.ai.projects.models.BLOB_REFERENCE """ @@ -8064,7 +11429,8 @@ class PendingUploadResponse(_Model): pending_upload_type: Literal[PendingUploadType.BLOB_REFERENCE] = rest_field( name="pendingUploadType", visibility=["read", "create", "update", "delete", "query"] ) - """BlobReference is the only supported type. Required. Blob Reference is the only supported type.""" + """The type of pending upload. Required. Deprecated: the service never read this value and + silently ignored it. Use TemporaryBlobReference instead.""" @overload def __init__( @@ -8087,6 +11453,171 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ProceduralMemoryItem(MemoryItem, discriminator="procedural"): + """A memory item containing a procedure extracted from conversations. + + :ivar memory_id: The unique ID of the memory item. Required. + :vartype memory_id: str + :ivar updated_at: The last update time of the memory item. Required. + :vartype updated_at: ~datetime.datetime + :ivar scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :vartype scope: str + :ivar content: The content of the memory. Required. + :vartype content: str + :ivar kind: The kind of the memory item. Required. Routine procedures extracted from + conversations. + :vartype kind: str or ~azure.ai.projects.models.PROCEDURAL + """ + + kind: Literal[MemoryItemKind.PROCEDURAL] = rest_discriminator(name="kind", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The kind of the memory item. Required. Routine procedures extracted from conversations.""" + + @overload + def __init__( + self, + *, + memory_id: str, + updated_at: datetime.datetime, + scope: str, + content: 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) + 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. + + :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 this candidate was promoted to. Required. + :vartype agent_name: str + :ivar agent_version: Version of the Foundry agent this candidate was promoted to. Required. + :vartype agent_version: str + """ + + 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 this candidate was promoted to. Required.""" + agent_version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Version of the Foundry agent this candidate was promoted to. Required.""" + + @overload + def __init__( + self, + *, + 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 PromptAgentDefinition(AgentDefinition, discriminator="prompt"): """The prompt agent definition. @@ -8100,14 +11631,13 @@ class PromptAgentDefinition(AgentDefinition, discriminator="prompt"): :vartype instructions: str :ivar temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and - deterministic. We generally recommend altering this or ``top_p`` but not both. + deterministic. We generally recommend altering this or ``top_p`` but not both. Defaults to + ``1``. :vartype temperature: float - :ivar top_p: An alternative to sampling with temperature, called nucleus sampling, - where the model considers the results of the tokens with top_p probability - mass. So 0.1 means only the tokens comprising the top 10% probability mass - are considered. - - We generally recommend altering this or ``temperature`` but not both. + :ivar top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 means only the + tokens comprising the top 10% probability mass are considered. We generally recommend altering + this or ``temperature`` but not both. Defaults to ``1``. :vartype top_p: float :ivar reasoning: :vartype reasoning: ~azure.ai.projects.models.Reasoning @@ -8135,14 +11665,12 @@ class PromptAgentDefinition(AgentDefinition, discriminator="prompt"): temperature: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. We - generally recommend altering this or ``top_p`` but not both.""" + generally recommend altering this or ``top_p`` but not both. Defaults to ``1``.""" top_p: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """An alternative to sampling with temperature, called nucleus sampling, - where the model considers the results of the tokens with top_p probability - mass. So 0.1 means only the tokens comprising the top 10% probability mass - are considered. - - We generally recommend altering this or ``temperature`` but not both.""" + """An alternative to sampling with temperature, called nucleus sampling, where the model considers + the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising + the top 10% probability mass are considered. We generally recommend altering this or + ``temperature`` but not both. Defaults to ``1``.""" reasoning: Optional["_models.Reasoning"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) tools: Optional[list["_models.Tool"]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """An array of tools the model may call while generating a response. You can specify which tool to @@ -8235,23 +11763,108 @@ class PromptBasedEvaluatorDefinition(EvaluatorDefinition, discriminator="prompt" :vartype metrics: dict[str, ~azure.ai.projects.models.EvaluatorMetric] :ivar type: Required. Prompt-based definition. :vartype type: str or ~azure.ai.projects.models.PROMPT - :ivar prompt_text: The prompt text used for evaluation. Required. - :vartype prompt_text: str + :ivar prompt_text: The prompt text used for evaluation. Required. + :vartype prompt_text: str + """ + + type: Literal[EvaluatorDefinitionType.PROMPT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. Prompt-based definition.""" + prompt_text: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The prompt text used for evaluation. Required.""" + + @overload + def __init__( + self, + *, + prompt_text: str, + init_parameters: Optional[dict[str, Any]] = None, + data_schema: Optional[dict[str, Any]] = None, + metrics: Optional[dict[str, "_models.EvaluatorMetric"]] = 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 = EvaluatorDefinitionType.PROMPT # type: ignore + + +class PromptDataGenerationJobSource(DataGenerationJobSource, discriminator="prompt"): + """Prompt source for data generation jobs — inline text provided by the user. + + :ivar description: Optional description of what this source represents — helps the pipeline + interpret its content (e.g., 'Company refund policy document' or 'Describes the agent's core + capabilities'). + :vartype description: str + :ivar type: The source type for this source, which is Prompt. Required. Prompt source — inline + text provided by the user. + :vartype type: str or ~azure.ai.projects.models.PROMPT + :ivar prompt: Inline prompt text (e.g., agent description, policy text, supplementary context). + Required. + :vartype prompt: str + """ + + type: Literal[DataGenerationJobSourceType.PROMPT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The source type for this source, which is Prompt. Required. Prompt source — inline text + provided by the user.""" + prompt: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Inline prompt text (e.g., agent description, policy text, supplementary context). Required.""" + + @overload + def __init__( + self, + *, + prompt: str, + description: 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) + self.type = DataGenerationJobSourceType.PROMPT # type: ignore + + +class PromptEvaluatorGenerationJobSource(EvaluatorGenerationJobSource, discriminator="prompt"): + """Prompt source for evaluator generation jobs — inline text provided by the user. + + :ivar description: Optional description of what this source represents — helps the pipeline + interpret its content (e.g., 'Company refund policy document' or 'Describes the agent's core + capabilities'). + :vartype description: str + :ivar type: The source type for this source, which is Prompt. Required. Prompt source — inline + text provided by the user. + :vartype type: str or ~azure.ai.projects.models.PROMPT + :ivar prompt: Inline prompt text (e.g., agent description, policy text, supplementary context). + Required. + :vartype prompt: str """ - type: Literal[EvaluatorDefinitionType.PROMPT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore - """Required. Prompt-based definition.""" - prompt_text: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The prompt text used for evaluation. Required.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional description of what this source represents — helps the pipeline interpret its content + (e.g., 'Company refund policy document' or 'Describes the agent's core capabilities').""" + type: Literal[EvaluatorGenerationJobSourceType.PROMPT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The source type for this source, which is Prompt. Required. Prompt source — inline text + provided by the user.""" + prompt: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Inline prompt text (e.g., agent description, policy text, supplementary context). Required.""" @overload def __init__( self, *, - prompt_text: str, - init_parameters: Optional[dict[str, Any]] = None, - data_schema: Optional[dict[str, Any]] = None, - metrics: Optional[dict[str, "_models.EvaluatorMetric"]] = None, + prompt: str, + description: Optional[str] = None, ) -> None: ... @overload @@ -8263,14 +11876,14 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.type = EvaluatorDefinitionType.PROMPT # type: ignore + self.type = EvaluatorGenerationJobSourceType.PROMPT # type: ignore 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", and "invocations". + "responses", "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 @@ -8279,8 +11892,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\", and - \"invocations\".""" + """The protocol type. Required. Known values are: \"activity_protocol\", \"responses\", \"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.""" @@ -8436,7 +12049,7 @@ class RecurrenceTrigger(Trigger, discriminator="Recurrence"): :vartype start_time: ~datetime.datetime :ivar end_time: End time for the recurrence schedule in ISO 8601 format. :vartype end_time: ~datetime.datetime - :ivar time_zone: Time zone for the recurrence schedule. + :ivar time_zone: Time zone for the recurrence schedule. Defaults to ``UTC``. :vartype time_zone: str :ivar interval: Interval for the recurrence schedule. Required. :vartype interval: int @@ -8455,7 +12068,7 @@ class RecurrenceTrigger(Trigger, discriminator="Recurrence"): ) """End time for the recurrence schedule in ISO 8601 format.""" time_zone: Optional[str] = rest_field(name="timeZone", visibility=["read", "create", "update", "delete", "query"]) - """Time zone for the recurrence schedule.""" + """Time zone for the recurrence schedule. Defaults to ``UTC``.""" interval: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Interval for the recurrence schedule. Required.""" schedule: "_models.RecurrenceSchedule" = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -8511,7 +12124,7 @@ class RedTeam(_Model): :ivar status: Status of the red-team. It is set by service and is read-only. :vartype status: str :ivar target: Target configuration for the red-team run. Required. - :vartype target: ~azure.ai.projects.models.TargetConfig + :vartype target: ~azure.ai.projects.models.RedTeamTargetConfig """ name: str = rest_field(name="id", visibility=["read"]) @@ -8546,14 +12159,14 @@ class RedTeam(_Model): removed.""" status: Optional[str] = rest_field(visibility=["read"]) """Status of the red-team. It is set by service and is read-only.""" - target: "_models.TargetConfig" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + target: "_models.RedTeamTargetConfig" = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Target configuration for the red-team run. Required.""" @overload def __init__( self, *, - target: "_models.TargetConfig", + target: "_models.RedTeamTargetConfig", display_name: Optional[str] = None, num_turns: Optional[int] = None, attack_strategies: Optional[list[Union[str, "_models.AttackStrategy"]]] = None, @@ -8631,6 +12244,296 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class Routine(_Model): + """A routine definition returned by the service. + + :ivar name: The routine name. + :vartype name: str + :ivar description: A human-readable description of the routine. + :vartype description: str + :ivar enabled: Whether the routine is enabled. Required. + :vartype enabled: bool + :ivar triggers: The triggers configured for the routine. + :vartype triggers: dict[str, ~azure.ai.projects.models.RoutineTrigger] + :ivar action: The action executed when the routine fires. + :vartype action: ~azure.ai.projects.models.RoutineAction + :ivar created_at: The time when the routine was created. + :vartype created_at: ~datetime.datetime + :ivar updated_at: The time when the routine was last updated. + :vartype updated_at: ~datetime.datetime + """ + + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The routine name.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A human-readable description of the routine.""" + enabled: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the routine is enabled. Required.""" + triggers: Optional[dict[str, "_models.RoutineTrigger"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The triggers configured for the routine.""" + action: Optional["_models.RoutineAction"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The action executed when the routine fires.""" + created_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The time when the routine was created.""" + updated_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The time when the routine was last updated.""" + + @overload + def __init__( + self, + *, + enabled: bool, + name: Optional[str] = None, + description: Optional[str] = None, + triggers: Optional[dict[str, "_models.RoutineTrigger"]] = None, + action: Optional["_models.RoutineAction"] = None, + created_at: Optional[datetime.datetime] = None, + updated_at: Optional[datetime.datetime] = 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 RoutineRun(_Model): + """A single routine run returned from the run history API. + + :ivar id: The unique run identifier for the routine attempt. Required. + :vartype id: str + :ivar status: The run status. + :vartype status: str + :ivar phase: The AgentExtensions lifecycle phase for the routine attempt. Known values are: + "queued", "dispatching", "completed", and "failed". + :vartype phase: str or ~azure.ai.projects.models.RoutineRunPhase + :ivar trigger_type: The trigger type that produced the routine attempt. Known values are: + "custom", "github_issue", "schedule", and "timer". + :vartype trigger_type: str or ~azure.ai.projects.models.RoutineTriggerType + :ivar trigger_name: The configured trigger name that produced the routine attempt. + :vartype trigger_name: str + :ivar attempt_source: The source path that created the routine attempt. Known values are: + "event_fire", "manual_dispatch", "queued_dispatch", "schedule_delivery", and "timer_delivery". + :vartype attempt_source: str or ~azure.ai.projects.models.RoutineAttemptSource + :ivar action_type: The action type dispatched for the routine attempt. Known values are: + "invoke_agent_responses_api" and "invoke_agent_invocations_api". + :vartype action_type: str or ~azure.ai.projects.models.RoutineActionType + :ivar agent_id: The project-scoped agent identifier recorded for the routine attempt. + :vartype agent_id: str + :ivar agent_endpoint_id: The legacy endpoint-scoped agent identifier recorded for the routine + attempt. + :vartype agent_endpoint_id: str + :ivar conversation_id: The conversation identifier used by a responses API dispatch. + :vartype conversation_id: str + :ivar session_id: The hosted-agent session identifier used by an invocations API dispatch. + :vartype session_id: str + :ivar triggered_at: The logical trigger time recorded for the routine attempt. + :vartype triggered_at: ~datetime.datetime + :ivar scheduled_fire_at: The scheduled fire time recorded for timer and schedule deliveries. + :vartype scheduled_fire_at: ~datetime.datetime + :ivar started_at: The time when the underlying run started. + :vartype started_at: ~datetime.datetime + :ivar ended_at: The time when the underlying run reached a terminal state. + :vartype ended_at: ~datetime.datetime + :ivar dispatch_id: The dispatch identifier associated with the routine attempt. + :vartype dispatch_id: str + :ivar action_correlation_id: The downstream action correlation identifier, when available. + :vartype action_correlation_id: str + :ivar response_id: The downstream response or invocation identifier, when available. + :vartype response_id: str + :ivar task_id: The workspace task identifier linked to the routine attempt, when available. + :vartype task_id: str + :ivar error_status_code: The downstream error status code captured for a failed attempt, when + available. + :vartype error_status_code: int + :ivar error_type: The fully qualified error type captured for a failed attempt, when available. + :vartype error_type: str + :ivar error_message: The truncated failure message captured for a failed attempt, when + available. + :vartype error_message: str + """ + + 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.""" + phase: Optional[Union[str, "_models.RoutineRunPhase"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The AgentExtensions lifecycle phase for the routine attempt. Known values are: \"queued\", + \"dispatching\", \"completed\", and \"failed\".""" + trigger_type: Optional[Union[str, "_models.RoutineTriggerType"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The trigger type that produced the routine attempt. Known values are: \"custom\", + \"github_issue\", \"schedule\", and \"timer\".""" + trigger_name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The configured trigger name that produced the routine attempt.""" + attempt_source: Optional[Union[str, "_models.RoutineAttemptSource"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The source path that created the routine attempt. Known values are: \"event_fire\", + \"manual_dispatch\", \"queued_dispatch\", \"schedule_delivery\", and \"timer_delivery\".""" + action_type: Optional[Union[str, "_models.RoutineActionType"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The action type dispatched for the routine attempt. Known values are: + \"invoke_agent_responses_api\" and \"invoke_agent_invocations_api\".""" + agent_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The project-scoped agent identifier recorded for the routine attempt.""" + agent_endpoint_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The legacy endpoint-scoped agent identifier recorded for the routine attempt.""" + conversation_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The conversation identifier used by a responses API dispatch.""" + session_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The hosted-agent session identifier used by an invocations API dispatch.""" + triggered_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The logical trigger time recorded for the routine attempt.""" + scheduled_fire_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The scheduled fire time recorded for timer and schedule deliveries.""" + started_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The time when the underlying run started.""" + ended_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The time when the underlying run reached a terminal state.""" + dispatch_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The dispatch identifier associated with the routine attempt.""" + action_correlation_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The downstream action correlation identifier, when available.""" + response_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The downstream response or invocation identifier, when available.""" + task_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The workspace task identifier linked to the routine attempt, when available.""" + error_status_code: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The downstream error status code captured for a failed attempt, when available.""" + error_type: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The fully qualified error type captured for a failed attempt, when available.""" + error_message: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The truncated failure message captured for a failed attempt, when available.""" + + @overload + def __init__( + self, + *, + status: Optional[str] = None, + phase: Optional[Union[str, "_models.RoutineRunPhase"]] = None, + trigger_type: Optional[Union[str, "_models.RoutineTriggerType"]] = None, + trigger_name: Optional[str] = None, + attempt_source: Optional[Union[str, "_models.RoutineAttemptSource"]] = None, + action_type: Optional[Union[str, "_models.RoutineActionType"]] = None, + agent_id: Optional[str] = None, + agent_endpoint_id: Optional[str] = None, + conversation_id: Optional[str] = None, + session_id: Optional[str] = None, + triggered_at: Optional[datetime.datetime] = None, + scheduled_fire_at: Optional[datetime.datetime] = None, + started_at: Optional[datetime.datetime] = None, + ended_at: Optional[datetime.datetime] = None, + dispatch_id: Optional[str] = None, + action_correlation_id: Optional[str] = None, + response_id: Optional[str] = None, + task_id: Optional[str] = None, + error_status_code: Optional[int] = None, + error_type: Optional[str] = None, + error_message: 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 RubricBasedEvaluatorDefinition(EvaluatorDefinition, discriminator="rubric"): + """Rubric-based evaluator definition — stores dimensions produced by the generate API. Used for + both quality and safety evaluators. + + :ivar init_parameters: The JSON schema (Draft 2020-12) for the evaluator's input parameters. + This includes parameters like type, properties, required. + :vartype init_parameters: dict[str, any] + :ivar data_schema: The JSON schema (Draft 2020-12) for the evaluator's input data. This + includes parameters like type, properties, required. + :vartype data_schema: dict[str, any] + :ivar metrics: List of output metrics produced by this evaluator. + :vartype metrics: dict[str, ~azure.ai.projects.models.EvaluatorMetric] + :ivar type: Required. Rubric-based evaluator definition. Stores dimensions (the scoring + blueprint) for both quality and safety evaluators. Can be created via the generate API or + manually via createVersion. + :vartype type: str or ~azure.ai.projects.models.RUBRIC + :ivar dimensions: The set of dimensions — the scoring blueprint used by the LLM judge. Quality + evaluators include a non-editable residual dimension with id 'general_quality' + (always_applicable: true); safety evaluators include 'general_policy_compliance'. Both use the + same Dimension structure. Required. + :vartype dimensions: list[~azure.ai.projects.models.Dimension] + :ivar pass_threshold: Pass/fail threshold for the aggregate rubric score, on the same + normalized 0.0-1.0 scale as the emitted ``score``. When the runtime weighted average meets or + exceeds this value, the result is ``pass``. Defaults to 0.5 (equivalent to a raw 1-5 weighted + average of 3.0). The 'any dimension scored 1 → fail' rule still applies regardless of this + threshold. + :vartype pass_threshold: float + """ + + type: Literal[EvaluatorDefinitionType.RUBRIC] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. Rubric-based evaluator definition. Stores dimensions (the scoring blueprint) for both + quality and safety evaluators. Can be created via the generate API or manually via + createVersion.""" + dimensions: list["_models.Dimension"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The set of dimensions — the scoring blueprint used by the LLM judge. Quality evaluators include + a non-editable residual dimension with id 'general_quality' (always_applicable: true); safety + evaluators include 'general_policy_compliance'. Both use the same Dimension structure. + Required.""" + pass_threshold: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Pass/fail threshold for the aggregate rubric score, on the same normalized 0.0-1.0 scale as the + emitted ``score``. When the runtime weighted average meets or exceeds this value, the result is + ``pass``. Defaults to 0.5 (equivalent to a raw 1-5 weighted average of 3.0). The 'any dimension + scored 1 → fail' rule still applies regardless of this threshold.""" + + @overload + def __init__( + self, + *, + dimensions: list["_models.Dimension"], + init_parameters: Optional[dict[str, Any]] = None, + data_schema: Optional[dict[str, Any]] = None, + metrics: Optional[dict[str, "_models.EvaluatorMetric"]] = None, + pass_threshold: Optional[float] = 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 = EvaluatorDefinitionType.RUBRIC # type: ignore + + class SASCredentials(BaseCredentials, discriminator="SAS"): """Shared Access Signature (SAS) credential definition. @@ -8740,6 +12643,46 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ScheduleRoutineTrigger(RoutineTrigger, discriminator="schedule"): + """A recurring cron-based routine trigger. + + :ivar type: The trigger type. Required. A recurring cron-based trigger. + :vartype type: str or ~azure.ai.projects.models.SCHEDULE + :ivar cron_expression: A 5-field cron expression. The service enforces a minimum interval of + five minutes by default. Required. + :vartype cron_expression: str + :ivar time_zone: An IANA or Windows time zone identifier for the schedule. Required. + :vartype time_zone: str + """ + + type: Literal[RoutineTriggerType.SCHEDULE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The trigger type. Required. A recurring cron-based trigger.""" + cron_expression: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A 5-field cron expression. The service enforces a minimum interval of five minutes by default. + Required.""" + time_zone: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """An IANA or Windows time zone identifier for the schedule. Required.""" + + @overload + def __init__( + self, + *, + cron_expression: str, + time_zone: 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) + self.type = RoutineTriggerType.SCHEDULE # type: ignore + + class ScheduleRun(_Model): """Schedule run model. @@ -8836,42 +12779,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class SessionDirectoryListResponse(_Model): - """Response from listing a directory in a session sandbox. - - :ivar path: The path that was listed, relative to the session home directory. Required. - :vartype path: str - :ivar entries: The directory entries. Required. - :vartype entries: list[~azure.ai.projects.models.SessionDirectoryEntry] - """ - - path: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The path that was listed, relative to the session home directory. Required.""" - entries: list["_models.SessionDirectoryEntry"] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """The directory entries. Required.""" - - @overload - def __init__( - self, - *, - path: str, - entries: list["_models.SessionDirectoryEntry"], - ) -> 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 SessionFileWriteResponse(_Model): +class SessionFileWriteResult(_Model): """Response from uploading a file to a session sandbox. :ivar path: The path where the file was written, relative to the session home directory. @@ -8963,25 +12871,130 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class SharepointGroundingToolParameters(_Model): - """The sharepoint grounding tool parameters. +class SharepointGroundingToolParameters(_Model): + """The sharepoint grounding tool parameters. + + :ivar project_connections: The project connections attached to this tool. There can be a + maximum of 1 connection resource attached to the tool. + :vartype project_connections: list[~azure.ai.projects.models.ToolProjectConnection] + """ + + project_connections: Optional[list["_models.ToolProjectConnection"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The project connections attached to this tool. There can be a maximum of 1 connection resource + attached to the tool.""" + + @overload + def __init__( + self, + *, + project_connections: Optional[list["_models.ToolProjectConnection"]] = 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 SharepointPreviewTool(Tool, discriminator="sharepoint_grounding_preview"): + """The input definition information for a sharepoint tool as used to configure an agent. + + :ivar type: The object type, which is always 'sharepoint_grounding_preview'. Required. + SHAREPOINT_GROUNDING_PREVIEW. + :vartype type: str or ~azure.ai.projects.models.SHAREPOINT_GROUNDING_PREVIEW + :ivar name: Optional user-defined name for this tool or configuration. + :vartype name: str + :ivar description: Optional user-defined description for this tool or configuration. + :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] + :ivar sharepoint_grounding_preview: The sharepoint grounding tool parameters. Required. + :vartype sharepoint_grounding_preview: + ~azure.ai.projects.models.SharepointGroundingToolParameters + """ + + type: Literal[ToolType.SHAREPOINT_GROUNDING_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The object type, which is always 'sharepoint_grounding_preview'. Required. + SHAREPOINT_GROUNDING_PREVIEW.""" + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined name for this tool or configuration.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" + sharepoint_grounding_preview: "_models.SharepointGroundingToolParameters" = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The sharepoint grounding tool parameters. Required.""" + + @overload + def __init__( + self, + *, + sharepoint_grounding_preview: "_models.SharepointGroundingToolParameters", + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = 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.SHAREPOINT_GROUNDING_PREVIEW # type: ignore + + +class SimpleQnADataGenerationJobOptions(DataGenerationJobOptions, discriminator="simple_qna"): + """The options for a data generation job with SimpleQnA type. - :ivar project_connections: The project connections attached to this tool. There can be a - maximum of 1 connection resource attached to the tool. - :vartype project_connections: list[~azure.ai.projects.models.ToolProjectConnection] + :ivar max_samples: Maximum number of samples to generate. Required. + :vartype max_samples: int + :ivar train_split: The proportion of the generated data to be used for training when the data + is used for fine-tuning. The rest will be used for validation. Value should be between 0 and 1. + :vartype train_split: float + :ivar model_options: The LLM model options. + :vartype model_options: ~azure.ai.projects.models.DataGenerationModelOptions + :ivar type: The data generation job type, which is SimpleQnA for this model. Required. Simple + question and answers between user and agent. + :vartype type: str or ~azure.ai.projects.models.SIMPLE_QNA + :ivar question_types: The question types to generate. Used only for fine-tuning scenarios. + :vartype question_types: list[str or ~azure.ai.projects.models.SimpleQnAFineTuningQuestionType] """ - project_connections: Optional[list["_models.ToolProjectConnection"]] = rest_field( + type: Literal[DataGenerationJobType.SIMPLE_QNA] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The data generation job type, which is SimpleQnA for this model. Required. Simple question and + answers between user and agent.""" + question_types: Optional[list[Union[str, "_models.SimpleQnAFineTuningQuestionType"]]] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """The project connections attached to this tool. There can be a maximum of 1 connection resource - attached to the tool.""" + """The question types to generate. Used only for fine-tuning scenarios.""" @overload def __init__( self, *, - project_connections: Optional[list["_models.ToolProjectConnection"]] = None, + max_samples: int, + train_split: Optional[float] = None, + model_options: Optional["_models.DataGenerationModelOptions"] = None, + question_types: Optional[list[Union[str, "_models.SimpleQnAFineTuningQuestionType"]]] = None, ) -> None: ... @overload @@ -8993,32 +13006,52 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) + self.type = DataGenerationJobType.SIMPLE_QNA # type: ignore -class SharepointPreviewTool(Tool, discriminator="sharepoint_grounding_preview"): - """The input definition information for a sharepoint tool as used to configure an agent. +class SkillDetails(_Model): + """A skill resource. - :ivar type: The object type, which is always 'sharepoint_grounding_preview'. Required. - SHAREPOINT_GROUNDING_PREVIEW. - :vartype type: str or ~azure.ai.projects.models.SHAREPOINT_GROUNDING_PREVIEW - :ivar sharepoint_grounding_preview: The sharepoint grounding tool parameters. Required. - :vartype sharepoint_grounding_preview: - ~azure.ai.projects.models.SharepointGroundingToolParameters + :ivar id: The unique identifier of the skill. Required. + :vartype id: str + :ivar name: The unique name of the skill. Required. + :vartype name: str + :ivar description: A human-readable description of the skill. Required. + :vartype description: str + :ivar created_at: The Unix timestamp (seconds) when the skill was created. Required. + :vartype created_at: ~datetime.datetime + :ivar default_version: The default version for the skill. Can be changed via updateSkill. + Required. + :vartype default_version: str + :ivar latest_version: The latest version for the skill. Required. + :vartype latest_version: str """ - type: Literal[ToolType.SHAREPOINT_GROUNDING_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore - """The object type, which is always 'sharepoint_grounding_preview'. Required. - SHAREPOINT_GROUNDING_PREVIEW.""" - sharepoint_grounding_preview: "_models.SharepointGroundingToolParameters" = rest_field( - visibility=["read", "create", "update", "delete", "query"] + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique identifier of the skill. Required.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique name of the skill. Required.""" + description: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A human-readable description of the skill. Required.""" + created_at: datetime.datetime = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" ) - """The sharepoint grounding tool parameters. Required.""" + """The Unix timestamp (seconds) when the skill was created. Required.""" + default_version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The default version for the skill. Can be changed via updateSkill. Required.""" + latest_version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The latest version for the skill. Required.""" @overload def __init__( self, *, - sharepoint_grounding_preview: "_models.SharepointGroundingToolParameters", + id: str, # pylint: disable=redefined-builtin + name: str, + description: str, + created_at: datetime.datetime, + default_version: str, + latest_version: str, ) -> None: ... @overload @@ -9030,54 +13063,52 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.type = ToolType.SHAREPOINT_GROUNDING_PREVIEW # type: ignore -class SkillObject(_Model): - """A skill object. +class SkillInlineContent(_Model): + """Inline content for defining a simple skill without uploading files. Follows the agentskills.io + SKILL.md specification. - :ivar skill_id: The unique identifier of the skill. Required. - :vartype skill_id: str - :ivar has_blob: Whether the skill was created from a zip blob package. Required. - :vartype has_blob: bool - :ivar name: The unique name of the skill. Required. - :vartype name: str - :ivar description: A human-readable description of the skill. + :ivar description: A human-readable description of what the skill does and when to use it. + Required. :vartype description: str - :ivar metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. + :ivar instructions: The skill instructions in markdown format. This is the body content of the + SKILL.md file. Required. + :vartype instructions: str + :ivar license: License name or reference to a bundled license file. + :vartype license: str + :ivar compatibility: Environment requirements or compatibility notes for the skill. + :vartype compatibility: str + :ivar metadata: Arbitrary key-value metadata for additional properties. :vartype metadata: dict[str, str] + :ivar allowed_tools: List of pre-approved tools the skill may use. Experimental. + :vartype allowed_tools: list[str] """ - skill_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The unique identifier of the skill. Required.""" - has_blob: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Whether the skill was created from a zip blob package. Required.""" - name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The unique name of the skill. Required.""" - description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """A human-readable description of the skill.""" + description: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A human-readable description of what the skill does and when to use it. Required.""" + instructions: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The skill instructions in markdown format. This is the body content of the SKILL.md file. + Required.""" + license: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """License name or reference to a bundled license file.""" + compatibility: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Environment requirements or compatibility notes for the skill.""" metadata: Optional[dict[str, str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters.""" + """Arbitrary key-value metadata for additional properties.""" + allowed_tools: Optional[list[str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """List of pre-approved tools the skill may use. Experimental.""" @overload def __init__( self, *, - skill_id: str, - has_blob: bool, - name: str, - description: Optional[str] = None, + description: str, + instructions: str, + license: Optional[str] = None, + compatibility: Optional[str] = None, metadata: Optional[dict[str, str]] = None, + allowed_tools: Optional[list[str]] = None, ) -> None: ... @overload @@ -9129,6 +13160,61 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = ContainerSkillType.SKILL_REFERENCE # type: ignore +class SkillVersion(_Model): + """A specific version of a skill. + + :ivar id: The unique identifier of the skill version. Required. + :vartype id: str + :ivar skill_id: The identifier of the parent skill. Required. + :vartype skill_id: str + :ivar name: The name of the skill version. Required. + :vartype name: str + :ivar version: The version identifier. Skill versions are immutable. Required. + :vartype version: str + :ivar description: A human-readable description of the skill version. Required. + :vartype description: str + :ivar created_at: The Unix timestamp (seconds) when the skill version was created. Required. + :vartype created_at: ~datetime.datetime + """ + + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique identifier of the skill version. Required.""" + skill_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The identifier of the parent skill. Required.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the skill version. Required.""" + version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The version identifier. Skill versions are immutable. Required.""" + description: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A human-readable description of the skill version. Required.""" + created_at: datetime.datetime = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (seconds) when the skill version was created. Required.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + skill_id: str, + name: str, + version: str, + description: str, + created_at: datetime.datetime, + ) -> 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 ToolChoiceParam(_Model): """How the model should select which tool (or tools) to use when generating a response. See the ``tools`` parameter to see how to specify which tools the model can call. @@ -9233,7 +13319,8 @@ class StructuredInputDefinition(_Model): :vartype default_value: any :ivar schema: The JSON schema for the structured input (optional). :vartype schema: dict[str, any] - :ivar required: Whether the input property is required when the agent is invoked. + :ivar required: Whether the input property is required when the agent is invoked. Defaults to + ``false``. :vartype required: bool """ @@ -9244,7 +13331,7 @@ class StructuredInputDefinition(_Model): schema: Optional[dict[str, Any]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The JSON schema for the structured input (optional).""" required: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Whether the input property is required when the agent is invoked.""" + """Whether the input property is required when the agent is invoked. Defaults to ``false``.""" @overload def __init__( @@ -9312,6 +13399,55 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class SystemDataV3(_Model): + """System metadata for a resource. + + :ivar created_at: Timestamp of resource creation. + :vartype created_at: ~datetime.datetime + :ivar created_by: Identity that created the resource. + :vartype created_by: str + :ivar created_by_type: Type of identity that created the resource. + :vartype created_by_type: str + :ivar last_modified_at: Timestamp of last resource modification. + :vartype last_modified_at: ~datetime.datetime + """ + + created_at: Optional[datetime.datetime] = rest_field( + name="createdAt", visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """Timestamp of resource creation.""" + created_by: Optional[str] = rest_field(name="createdBy", visibility=["read", "create", "update", "delete", "query"]) + """Identity that created the resource.""" + created_by_type: Optional[str] = rest_field( + name="createdByType", visibility=["read", "create", "update", "delete", "query"] + ) + """Type of identity that created the resource.""" + last_modified_at: Optional[datetime.datetime] = rest_field( + name="lastModifiedAt", visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """Timestamp of last resource modification.""" + + @overload + def __init__( + self, + *, + created_at: Optional[datetime.datetime] = None, + created_by: Optional[str] = None, + created_by_type: Optional[str] = None, + last_modified_at: Optional[datetime.datetime] = 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 TaxonomyCategory(_Model): """Taxonomy category definition. @@ -9578,12 +13714,166 @@ class TextResponseFormatText(TextResponseFormat, discriminator="text"): :vartype type: str or ~azure.ai.projects.models.TEXT """ - type: Literal[TextResponseFormatConfigurationType.TEXT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore - """The type of response format being defined. Always ``text``. Required. TEXT.""" + type: Literal[TextResponseFormatConfigurationType.TEXT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of response format being defined. Always ``text``. Required. TEXT.""" + + @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 = TextResponseFormatConfigurationType.TEXT # type: ignore + + +class TimerRoutineTrigger(RoutineTrigger, discriminator="timer"): + """A one-shot timer routine trigger. + + :ivar type: The trigger type. Required. A one-shot timer trigger. + :vartype type: str or ~azure.ai.projects.models.TIMER + :ivar at: The UTC date and time at which the timer fires. + :vartype at: ~datetime.datetime + """ + + type: Literal[RoutineTriggerType.TIMER] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The trigger type. Required. A one-shot timer trigger.""" + at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="rfc3339" + ) + """The UTC date and time at which the timer fires.""" + + @overload + def __init__( + self, + *, + at: Optional[datetime.datetime] = 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 = RoutineTriggerType.TIMER # type: ignore + + +class ToolboxObject(_Model): + """A toolbox that stores reusable tool definitions for agents. + + :ivar id: The unique identifier of the toolbox. Required. + :vartype id: str + :ivar name: The name of the toolbox. Required. + :vartype name: str + :ivar default_version: The version identifier that the toolbox currently points to. Defaults to + the latest version. Can be changed via updateToolbox. Required. + :vartype default_version: str + """ + + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique identifier of the toolbox. Required.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the toolbox. Required.""" + default_version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The version identifier that the toolbox currently points to. Defaults to the latest version. + Can be changed via updateToolbox. Required.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + name: str, + default_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 ToolboxPolicies(_Model): + """Policy configuration for a toolbox, including content safety and other governance settings. + + :ivar rai_config: Responsible AI content filtering configuration. + :vartype rai_config: ~azure.ai.projects.models.RaiConfig + """ + + rai_config: Optional["_models.RaiConfig"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Responsible AI content filtering configuration.""" + + @overload + def __init__( + self, + *, + rai_config: Optional["_models.RaiConfig"] = 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 ToolboxSearchPreviewTool(Tool, discriminator="toolbox_search_preview"): + """A tool for searching over the agent's toolbox. When present, deferred tools are hidden from + ``tools/list`` and only discoverable via ``search_tools`` queries at runtime. + + :ivar type: The type of the tool. Always ``toolbox_search_preview``. Required. + TOOLBOX_SEARCH_PREVIEW. + :vartype type: str or ~azure.ai.projects.models.TOOLBOX_SEARCH_PREVIEW + :ivar name: Optional user-defined name for this tool or configuration. + :vartype name: str + :ivar description: Optional user-defined description for this tool or configuration. + :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] + """ + + type: Literal[ToolType.TOOLBOX_SEARCH_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the tool. Always ``toolbox_search_preview``. Required. TOOLBOX_SEARCH_PREVIEW.""" + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined name for this tool or configuration.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" @overload def __init__( self, + *, + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -9595,36 +13885,28 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.type = TextResponseFormatConfigurationType.TEXT # type: ignore + self.type = ToolType.TOOLBOX_SEARCH_PREVIEW # type: ignore -class ToolboxObject(_Model): - """A toolbox that stores reusable tool definitions for agents. +class ToolboxSkill(_Model): + """A skill source included in a toolbox. - :ivar id: The unique identifier of the toolbox. Required. - :vartype id: str - :ivar name: The name of the toolbox. Required. - :vartype name: str - :ivar default_version: The version identifier that the toolbox currently points to. Defaults to - the latest version. Can be changed via updateToolbox. Required. - :vartype default_version: str + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + ToolboxSkillReference + + :ivar type: The type of skill source. Required. Default value is None. + :vartype type: str """ - id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The unique identifier of the toolbox. Required.""" - name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The name of the toolbox. Required.""" - default_version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The version identifier that the toolbox currently points to. Defaults to the latest version. - Can be changed via updateToolbox. Required.""" + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """The type of skill source. Required. Default value is None.""" @overload def __init__( self, *, - id: str, # pylint: disable=redefined-builtin - name: str, - default_version: str, + type: str, ) -> None: ... @overload @@ -9638,21 +13920,32 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class ToolboxPolicies(_Model): - """Policy configuration for a toolbox, including content safety and other governance settings. +class ToolboxSkillReference(ToolboxSkill, discriminator="skill_reference"): + """A reference to an existing skill to include in a toolbox. - :ivar rai_config: Responsible AI content filtering configuration. - :vartype rai_config: ~azure.ai.projects.models.RaiConfig + :ivar type: The type of skill source. Required. Default value is "skill_reference". + :vartype type: str + :ivar name: The name of the skill. Required. + :vartype name: str + :ivar version: The version of the skill. If not specified, the skill's default version is used. + When a version is specified, the reference is pinned to that immutable version. + :vartype version: str """ - rai_config: Optional["_models.RaiConfig"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Responsible AI content filtering configuration.""" + type: Literal["skill_reference"] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of skill source. Required. Default value is \"skill_reference\".""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the skill. Required.""" + version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The version of the skill. If not specified, the skill's default version is used. When a version + is specified, the reference is pinned to that immutable version.""" @overload def __init__( self, *, - rai_config: Optional["_models.RaiConfig"] = None, + name: str, + version: Optional[str] = None, ) -> None: ... @overload @@ -9664,6 +13957,7 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) + self.type = "skill_reference" # type: ignore class ToolboxVersionObject(_Model): @@ -9689,6 +13983,8 @@ class ToolboxVersionObject(_Model): :vartype created_at: ~datetime.datetime :ivar tools: The list of tools contained in this toolbox version. Required. :vartype tools: list[~azure.ai.projects.models.Tool] + :ivar skills: The list of skill sources included in this toolbox version. + :vartype skills: list[~azure.ai.projects.models.ToolboxSkill] :ivar policies: Policy configuration for the toolbox version. :vartype policies: ~azure.ai.projects.models.ToolboxPolicies """ @@ -9715,6 +14011,10 @@ class ToolboxVersionObject(_Model): """The Unix timestamp (seconds) when the toolbox version was created. Required.""" tools: list["_models.Tool"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The list of tools contained in this toolbox version. Required.""" + skills: Optional[list["_models.ToolboxSkill"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The list of skill sources included in this toolbox version.""" policies: Optional["_models.ToolboxPolicies"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -9731,6 +14031,7 @@ def __init__( created_at: datetime.datetime, tools: list["_models.Tool"], description: Optional[str] = None, + skills: Optional[list["_models.ToolboxSkill"]] = None, policies: Optional["_models.ToolboxPolicies"] = None, ) -> None: ... @@ -10078,6 +14379,45 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = ToolChoiceParamType.WEB_SEARCH_PREVIEW_2025_03_11 # type: ignore +class ToolConfig(_Model): + """Per-tool configuration that controls tool visibility and search behavior. + + :ivar pin: When true, the tool is always included in agent context and visible in + ``tools/list``. When false (default), the tool is hidden from ``tools/list`` and only + discoverable via ``tool_search``. + :vartype pin: bool + :ivar additional_search_text: Additional text indexed for tool_search. Supplements the native + tool description to improve discoverability. Does not alter ``tools/list`` output. + :vartype additional_search_text: str + """ + + pin: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """When true, the tool is always included in agent context and visible in ``tools/list``. When + false (default), the tool is hidden from ``tools/list`` and only discoverable via + ``tool_search``.""" + additional_search_text: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Additional text indexed for tool_search. Supplements the native tool description to improve + discoverability. Does not alter ``tools/list`` output.""" + + @overload + def __init__( + self, + *, + pin: Optional[bool] = None, + additional_search_text: 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 ToolDescription(_Model): """Description of a tool that can be used by an agent. @@ -10140,6 +14480,266 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ToolUseFineTuningDataGenerationJobOptions( + DataGenerationJobOptions, discriminator="tool_use" +): # pylint: disable=name-too-long + """The options for a data generation job with ToolUse type. Used only for fine-tuning scenarios. + + :ivar max_samples: Maximum number of samples to generate. Required. + :vartype max_samples: int + :ivar train_split: The proportion of the generated data to be used for training when the data + is used for fine-tuning. The rest will be used for validation. Value should be between 0 and 1. + :vartype train_split: float + :ivar model_options: The LLM model options. + :vartype model_options: ~azure.ai.projects.models.DataGenerationModelOptions + :ivar type: The data generation job type, which is ToolUse for this model. Required. Tool + calling conversation between user and agent. + :vartype type: str or ~azure.ai.projects.models.TOOL_USE + """ + + type: Literal[DataGenerationJobType.TOOL_USE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The data generation job type, which is ToolUse for this model. Required. Tool calling + conversation between user and agent.""" + + @overload + def __init__( + self, + *, + max_samples: int, + train_split: Optional[float] = None, + model_options: Optional["_models.DataGenerationModelOptions"] = 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 = DataGenerationJobType.TOOL_USE # type: ignore + + +class TracesDataGenerationJobOptions(DataGenerationJobOptions, discriminator="traces"): + """The options for a data generation job with Traces type. + + :ivar max_samples: Maximum number of samples to generate. Required. + :vartype max_samples: int + :ivar train_split: The proportion of the generated data to be used for training when the data + is used for fine-tuning. The rest will be used for validation. Value should be between 0 and 1. + :vartype train_split: float + :ivar model_options: The LLM model options. + :vartype model_options: ~azure.ai.projects.models.DataGenerationModelOptions + :ivar type: The data generation job type, which is Traces for this model. Required. Single turn + query and response from agent traces. + :vartype type: str or ~azure.ai.projects.models.TRACES + """ + + type: Literal[DataGenerationJobType.TRACES] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The data generation job type, which is Traces for this model. Required. Single turn query and + response from agent traces.""" + + @overload + def __init__( + self, + *, + max_samples: int, + train_split: Optional[float] = None, + model_options: Optional["_models.DataGenerationModelOptions"] = 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 = DataGenerationJobType.TRACES # type: ignore + + +class TracesDataGenerationJobSource(DataGenerationJobSource, discriminator="traces"): + """Traces source for data generation jobs — conversation traces from Application Insights. + + :ivar description: Optional description of what this source represents — helps the pipeline + interpret its content (e.g., 'Company refund policy document' or 'Describes the agent's core + capabilities'). + :vartype description: str + :ivar type: The source type for this source, which is Traces. Required. Traces source — + conversation traces from Application Insights. + :vartype type: str or ~azure.ai.projects.models.TRACES + :ivar agent_id: The unique agent ID used to filter traces. Provide either ``agent_id`` or + ``agent_name`` — at least one is required. + :vartype agent_id: str + :ivar agent_name: The agent name to fetch traces for. Provide either ``agent_id`` or + ``agent_name`` — at least one is required. + :vartype agent_name: str + :ivar agent_version: The agent version. If not specified, traces for ALL versions of the agent + are included within the time window. + :vartype agent_version: str + :ivar start_time: Start of the time window (Unix timestamp in seconds) for fetching traces. + Required. + :vartype start_time: ~datetime.datetime + :ivar end_time: End of the time window (Unix timestamp in seconds). Defaults to current time. + :vartype end_time: ~datetime.datetime + """ + + type: Literal[DataGenerationJobSourceType.TRACES] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The source type for this source, which is Traces. Required. Traces source — conversation traces + from Application Insights.""" + agent_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique agent ID used to filter traces. Provide either ``agent_id`` or ``agent_name`` — at + least one is required.""" + agent_name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The agent name to fetch traces for. Provide either ``agent_id`` or ``agent_name`` — at least + one is required.""" + agent_version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The agent version. If not specified, traces for ALL versions of the agent are included within + the time window.""" + start_time: datetime.datetime = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """Start of the time window (Unix timestamp in seconds) for fetching traces. Required.""" + end_time: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """End of the time window (Unix timestamp in seconds). Defaults to current time.""" + + @overload + def __init__( + self, + *, + start_time: datetime.datetime, + description: Optional[str] = None, + agent_id: Optional[str] = None, + agent_name: Optional[str] = None, + agent_version: Optional[str] = None, + end_time: Optional[datetime.datetime] = 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 = DataGenerationJobSourceType.TRACES # type: ignore + + +class TracesEvaluatorGenerationJobSource(EvaluatorGenerationJobSource, discriminator="traces"): + """Traces source for evaluator generation jobs — conversation traces from Application Insights. + + :ivar description: Optional description of what this source represents — helps the pipeline + interpret its content (e.g., 'Company refund policy document' or 'Describes the agent's core + capabilities'). + :vartype description: str + :ivar type: The source type for this source, which is Traces. Required. Traces source — + conversation traces from Application Insights. + :vartype type: str or ~azure.ai.projects.models.TRACES + :ivar agent_id: The unique agent ID used to filter traces. Provide either ``agent_id`` or + ``agent_name`` — at least one is required. + :vartype agent_id: str + :ivar agent_name: The agent name to fetch traces for. Provide either ``agent_id`` or + ``agent_name`` — at least one is required. + :vartype agent_name: str + :ivar agent_version: The agent version. If not specified, traces for ALL versions of the agent + are included within the time window. + :vartype agent_version: str + :ivar start_time: Start of the time window (Unix timestamp in seconds) for fetching traces. + Required. + :vartype start_time: ~datetime.datetime + :ivar end_time: End of the time window (Unix timestamp in seconds). Defaults to current time. + :vartype end_time: ~datetime.datetime + """ + + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional description of what this source represents — helps the pipeline interpret its content + (e.g., 'Company refund policy document' or 'Describes the agent's core capabilities').""" + type: Literal[EvaluatorGenerationJobSourceType.TRACES] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The source type for this source, which is Traces. Required. Traces source — conversation traces + from Application Insights.""" + agent_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique agent ID used to filter traces. Provide either ``agent_id`` or ``agent_name`` — at + least one is required.""" + agent_name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The agent name to fetch traces for. Provide either ``agent_id`` or ``agent_name`` — at least + one is required.""" + agent_version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The agent version. If not specified, traces for ALL versions of the agent are included within + the time window.""" + start_time: datetime.datetime = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """Start of the time window (Unix timestamp in seconds) for fetching traces. Required.""" + end_time: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """End of the time window (Unix timestamp in seconds). Defaults to current time.""" + + @overload + def __init__( + self, + *, + start_time: datetime.datetime, + description: Optional[str] = None, + agent_id: Optional[str] = None, + agent_name: Optional[str] = None, + agent_version: Optional[str] = None, + end_time: Optional[datetime.datetime] = 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 = EvaluatorGenerationJobSourceType.TRACES # type: ignore + + +class UpdateModelVersionRequest(_Model): + """Request body for updating a model version. Only description and tags can be modified. + + :ivar description: The asset description text. + :vartype description: str + :ivar tags: Tag dictionary. Tags can be added, removed, and updated. + :vartype tags: dict[str, str] + """ + + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The asset description text.""" + tags: Optional[dict[str, str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Tag dictionary. Tags can be added, removed, and updated.""" + + @overload + def __init__( + self, + *, + description: Optional[str] = None, + tags: Optional[dict[str, 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 UpdateToolboxRequest(_Model): """UpdateToolboxRequest. @@ -10454,6 +15054,10 @@ class WebSearchTool(Tool, discriminator="web_search"): :vartype name: str :ivar description: Optional user-defined description for this tool or configuration. :vartype description: str + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] :ivar custom_search_configuration: The project connections attached to this tool. There can be a maximum of 1 connection resource attached to the tool. :vartype custom_search_configuration: ~azure.ai.projects.models.WebSearchConfiguration @@ -10478,6 +15082,12 @@ class WebSearchTool(Tool, discriminator="web_search"): """Optional user-defined name for this tool or configuration.""" description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Optional user-defined description for this tool or configuration.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" custom_search_configuration: Optional["_models.WebSearchConfiguration"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -10493,6 +15103,7 @@ def __init__( search_context_size: Optional[Literal["low", "medium", "high"]] = None, name: Optional[str] = None, description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, custom_search_configuration: Optional["_models.WebSearchConfiguration"] = None, ) -> None: ... @@ -10611,32 +15222,41 @@ class WorkIQPreviewTool(Tool, discriminator="work_iq_preview"): :ivar type: The object type, which is always 'work_iq_preview'. Required. WORK_IQ_PREVIEW. :vartype type: str or ~azure.ai.projects.models.WORK_IQ_PREVIEW + :ivar project_connection_id: The ID of the WorkIQ project connection. Required. + :vartype project_connection_id: str :ivar name: Optional user-defined name for this tool or configuration. :vartype name: str :ivar description: Optional user-defined description for this tool or configuration. :vartype description: str - :ivar work_iq_preview: The WorkIQ tool parameters. Required. - :vartype work_iq_preview: ~azure.ai.projects.models.WorkIQPreviewToolParameters + :ivar tool_configs: Per-tool configuration map. Keys are tool names or ``*`` (catch-all + default). Resolution order: exact tool name match takes priority over ``*``. Unknown tool names + are silently ignored at runtime. + :vartype tool_configs: dict[str, ~azure.ai.projects.models.ToolConfig] """ type: Literal[ToolType.WORK_IQ_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """The object type, which is always 'work_iq_preview'. Required. WORK_IQ_PREVIEW.""" + project_connection_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The ID of the WorkIQ project connection. Required.""" name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Optional user-defined name for this tool or configuration.""" description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Optional user-defined description for this tool or configuration.""" - work_iq_preview: "_models.WorkIQPreviewToolParameters" = rest_field( + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """The WorkIQ tool parameters. Required.""" + """Per-tool configuration map. Keys are tool names or ``*`` (catch-all default). Resolution order: + exact tool name match takes priority over ``*``. Unknown tool names are silently ignored at + runtime.""" @overload def __init__( self, *, - work_iq_preview: "_models.WorkIQPreviewToolParameters", + project_connection_id: str, name: Optional[str] = None, description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @overload @@ -10649,31 +15269,3 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.type = ToolType.WORK_IQ_PREVIEW # type: ignore - - -class WorkIQPreviewToolParameters(_Model): - """The WorkIQ tool parameters. - - :ivar project_connection_id: The ID of the WorkIQ project connection. Required. - :vartype project_connection_id: str - """ - - project_connection_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The ID of the WorkIQ project connection. Required.""" - - @overload - def __init__( - self, - *, - project_connection_id: 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) 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 a8b53026f7f3..12dba1e0a0e2 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 @@ -39,21 +39,36 @@ _FOUNDRY_FEATURES_HEADER_NAME: Final[str] = "Foundry-Features" """The HTTP header name used to opt in to Foundry preview features.""" +_PREVIEW_FEATURE_REQUIRED_CODE: Final = "preview_feature_required" +_PREVIEW_FEATURE_ADDED_ERROR_MESSAGE: Final = ( + '\n**Python SDK users**: This operation requires you to set "allow_preview=True" ' + "when calling the AIProjectClient constructor. " + "\nNote that preview features are under development and subject to change. They should not be used in production environments." +) +_AGENT_OPERATION_FEATURE_HEADERS: Final[str] = ",".join( + [ + _AgentDefinitionOptInKeys.HOSTED_AGENTS_V1_PREVIEW.value, + _AgentDefinitionOptInKeys.WORKFLOW_AGENTS_V1_PREVIEW.value, + _AgentDefinitionOptInKeys.AGENT_ENDPOINT_V1_PREVIEW.value, + _AgentDefinitionOptInKeys.CODE_AGENTS_V1_PREVIEW.value, + _AgentDefinitionOptInKeys.EXTERNAL_AGENTS_V1_PREVIEW.value, + _FoundryFeaturesOptInKeys.AGENTS_OPTIMIZATION_V1_PREVIEW.value, + ] +) + _BETA_OPERATION_FEATURE_HEADERS: Final[dict] = { "evaluation_taxonomies": _FoundryFeaturesOptInKeys.EVALUATIONS_V1_PREVIEW.value, "evaluators": _FoundryFeaturesOptInKeys.EVALUATIONS_V1_PREVIEW.value, "insights": _FoundryFeaturesOptInKeys.INSIGHTS_V1_PREVIEW.value, "memory_stores": _FoundryFeaturesOptInKeys.MEMORY_STORES_V1_PREVIEW.value, + "models": _FoundryFeaturesOptInKeys.MODELS_V1_PREVIEW.value, "red_teams": _FoundryFeaturesOptInKeys.RED_TEAMS_V1_PREVIEW.value, + "routines": _FoundryFeaturesOptInKeys.ROUTINES_V1_PREVIEW.value, "schedules": _FoundryFeaturesOptInKeys.SCHEDULES_V1_PREVIEW.value, - "toolboxes": _FoundryFeaturesOptInKeys.TOOLBOXES_V1_PREVIEW.value, + "toolboxes": "Toolboxes=V1Preview", "skills": _FoundryFeaturesOptInKeys.SKILLS_V1_PREVIEW.value, - "agents": ",".join( - [ - _AgentDefinitionOptInKeys.HOSTED_AGENTS_V1_PREVIEW.value, - _AgentDefinitionOptInKeys.AGENT_ENDPOINT_V1_PREVIEW.value, - ] - ), + "datasets": _FoundryFeaturesOptInKeys.DATA_GENERATION_JOBS_V1_PREVIEW.value, + "agents": _AGENT_OPERATION_FEATURE_HEADERS, } """Foundry-Features header values keyed by beta sub-client property name.""" 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 52f7a21b1821..72f5c1fd7421 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 @@ -35,8 +35,9 @@ from .. import models as _models from .._configuration import AIProjectClientConfiguration -from .._utils.model_base import SdkJSONEncoder, _deserialize, _failsafe_deserialize +from .._utils.model_base import Model as _Model, SdkJSONEncoder, _deserialize, _failsafe_deserialize from .._utils.serialization import Deserializer, Serializer +from .._utils.utils import prepare_multipart_form_data JSON = MutableMapping[str, Any] _Unset: Any = object() @@ -72,7 +73,7 @@ def build_agents_get_request(agent_name: str, **kwargs: Any) -> HttpRequest: return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_agents_delete_request(agent_name: str, **kwargs: Any) -> HttpRequest: +def build_agents_delete_request(agent_name: str, *, force: Optional[bool] = None, **kwargs: Any) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -88,6 +89,8 @@ def build_agents_delete_request(agent_name: str, **kwargs: Any) -> HttpRequest: _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") # Construct headers @@ -214,7 +217,9 @@ def build_agents_get_version_request(agent_name: str, agent_version: str, **kwar return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_agents_delete_version_request(agent_name: str, agent_version: str, **kwargs: Any) -> HttpRequest: +def build_agents_delete_version_request( + agent_name: str, agent_version: str, *, force: Optional[bool] = None, **kwargs: Any +) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -231,6 +236,8 @@ def build_agents_delete_version_request(agent_name: str, agent_version: str, **k _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") # Construct headers @@ -826,7 +833,64 @@ def build_beta_agents_patch_agent_details_request( # pylint: disable=name-too-l return HttpRequest(method="PATCH", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_agents_create_session_request(agent_name: str, *, isolation_key: str, **kwargs: Any) -> HttpRequest: +def build_beta_agents_create_version_from_code_request( # pylint: disable=name-too-long + agent_name: str, *, code_zip_sha256: 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 = "/agents/{agent_name}/versions" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "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["x-ms-code-zip-sha256"] = _SERIALIZER.header("code_zip_sha256", code_zip_sha256, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_agents_download_code_request( + agent_name: str, *, agent_version: 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/zip") + + # Construct URL + _url = "/agents/{agent_name}/code:download" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + if agent_version is not None: + _params["agent_version"] = _SERIALIZER.query("agent_version", agent_version, "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_create_session_request( + agent_name: str, *, user_isolation_key: Optional[str] = None, **kwargs: Any +) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -846,7 +910,8 @@ def build_beta_agents_create_session_request(agent_name: str, *, isolation_key: _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") # Construct headers - _headers["x-session-isolation-key"] = _SERIALIZER.header("isolation_key", isolation_key, "str") + if user_isolation_key is not None: + _headers["x-ms-user-isolation-key"] = _SERIALIZER.header("user_isolation_key", user_isolation_key, "str") if content_type is not None: _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") @@ -854,7 +919,9 @@ def build_beta_agents_create_session_request(agent_name: str, *, isolation_key: return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_agents_get_session_request(agent_name: str, session_id: str, **kwargs: Any) -> HttpRequest: +def build_beta_agents_get_session_request( + agent_name: str, session_id: str, *, user_isolation_key: Optional[str] = None, **kwargs: Any +) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -874,13 +941,15 @@ def build_beta_agents_get_session_request(agent_name: str, session_id: str, **kw _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") # Construct headers + if user_isolation_key is not None: + _headers["x-ms-user-isolation-key"] = _SERIALIZER.header("user_isolation_key", user_isolation_key, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) def build_beta_agents_delete_session_request( - agent_name: str, session_id: str, *, isolation_key: str, **kwargs: Any + agent_name: str, session_id: str, *, user_isolation_key: Optional[str] = None, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -899,14 +968,35 @@ def build_beta_agents_delete_session_request( _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") # Construct headers - _headers["x-session-isolation-key"] = _SERIALIZER.header("isolation_key", isolation_key, "str") + if user_isolation_key is not None: + _headers["x-ms-user-isolation-key"] = _SERIALIZER.header("user_isolation_key", user_isolation_key, "str") return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) +def build_beta_agents_stop_session_request(agent_name: str, session_id: str, **kwargs: Any) -> HttpRequest: + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + # Construct URL + _url = "/agents/{agent_name}/endpoint/sessions/{session_id}:stop" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "session_id": _SERIALIZER.url("session_id", session_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + return HttpRequest(method="POST", url=_url, params=_params, **kwargs) + + def build_beta_agents_list_sessions_request( agent_name: str, *, + user_isolation_key: Optional[str] = None, limit: Optional[int] = None, order: Optional[Union[str, _models.PageOrder]] = None, after: Optional[str] = None, @@ -939,6 +1029,8 @@ def build_beta_agents_list_sessions_request( _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") # Construct headers + if user_isolation_key is not None: + _headers["x-ms-user-isolation-key"] = _SERIALIZER.header("user_isolation_key", user_isolation_key, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) @@ -973,7 +1065,7 @@ def build_beta_agents_get_session_log_stream_request( # pylint: disable=name-to def build_beta_agents_upload_session_file_request( # pylint: disable=name-too-long - agent_name: str, agent_session_id: str, *, path: str, **kwargs: Any + agent_name: str, agent_session_id: str, *, path: str, user_isolation_key: Optional[str] = None, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -997,13 +1089,15 @@ def build_beta_agents_upload_session_file_request( # pylint: disable=name-too-l # Construct headers _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + if user_isolation_key is not None: + _headers["x-ms-user-isolation-key"] = _SERIALIZER.header("user_isolation_key", user_isolation_key, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") return HttpRequest(method="PUT", url=_url, params=_params, headers=_headers, **kwargs) def build_beta_agents_download_session_file_request( # pylint: disable=name-too-long - agent_name: str, agent_session_id: str, *, path: str, **kwargs: Any + agent_name: str, agent_session_id: str, *, path: str, user_isolation_key: Optional[str] = None, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1025,13 +1119,24 @@ def build_beta_agents_download_session_file_request( # pylint: disable=name-too _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") # Construct headers + if user_isolation_key is not None: + _headers["x-ms-user-isolation-key"] = _SERIALIZER.header("user_isolation_key", user_isolation_key, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_agents_get_session_files_request( # pylint: disable=name-too-long - agent_name: str, agent_session_id: str, *, path: str, **kwargs: Any +def build_beta_agents_list_session_files_request( # pylint: disable=name-too-long + agent_name: str, + agent_session_id: str, + *, + path: Optional[str] = None, + user_isolation_key: Optional[str] = None, + 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 {}) @@ -1049,18 +1154,36 @@ def build_beta_agents_get_session_files_request( # pylint: disable=name-too-lon _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["path"] = _SERIALIZER.query("path", path, "str") + if path is not None: + _params["path"] = _SERIALIZER.query("path", path, "str") + 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 + if user_isolation_key is not None: + _headers["x-ms-user-isolation-key"] = _SERIALIZER.header("user_isolation_key", user_isolation_key, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) def build_beta_agents_delete_session_file_request( # pylint: disable=name-too-long - agent_name: str, agent_session_id: str, *, path: str, recursive: Optional[bool] = None, **kwargs: Any + agent_name: str, + agent_session_id: str, + *, + path: str, + recursive: Optional[bool] = None, + user_isolation_key: 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")) @@ -1079,22 +1202,52 @@ def build_beta_agents_delete_session_file_request( # pylint: disable=name-too-l _params["recursive"] = _SERIALIZER.query("recursive", recursive, "bool") _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) + # Construct headers + if user_isolation_key is not None: + _headers["x-ms-user-isolation-key"] = _SERIALIZER.header("user_isolation_key", user_isolation_key, "str") + return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_evaluation_taxonomies_get_request( # pylint: disable=name-too-long - name: str, **kwargs: Any + +def build_beta_agents_create_optimization_job_request( # pylint: disable=name-too-long + *, operation_id: Optional[str] = None, **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 = "/evaluationtaxonomies/{name}" + _url = "/agent_optimization_jobs" + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if operation_id is not None: + _headers["Operation-Id"] = _SERIALIZER.header("operation_id", operation_id, "str") + 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_agents_get_optimization_job_request( # pylint: disable=name-too-long + job_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}" path_format_arguments = { - "name": _SERIALIZER.url("name", name, "str"), + "jobId": _SERIALIZER.url("job_id", job_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -1108,8 +1261,15 @@ def build_beta_evaluation_taxonomies_get_request( # pylint: disable=name-too-lo return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_evaluation_taxonomies_list_request( # pylint: disable=name-too-long - *, input_name: Optional[str] = None, input_type: Optional[str] = None, **kwargs: Any +def build_beta_agents_list_optimization_jobs_request( # pylint: disable=name-too-long + *, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + after: Optional[str] = None, + before: Optional[str] = None, + status: Optional[Union[str, _models.JobStatus]] = None, + agent_name: Optional[str] = None, + **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1118,14 +1278,22 @@ def build_beta_evaluation_taxonomies_list_request( # pylint: disable=name-too-l accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/evaluationtaxonomies" + _url = "/agent_optimization_jobs" # 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") + if status is not None: + _params["status"] = _SERIALIZER.query("status", status, "str") + if agent_name is not None: + _params["agent_name"] = _SERIALIZER.query("agent_name", agent_name, "str") _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - if input_name is not None: - _params["inputName"] = _SERIALIZER.query("input_name", input_name, "str") - if input_type is not None: - _params["inputType"] = _SERIALIZER.query("input_type", input_type, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") @@ -1133,16 +1301,19 @@ def build_beta_evaluation_taxonomies_list_request( # pylint: disable=name-too-l return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_evaluation_taxonomies_delete_request( # pylint: disable=name-too-long - name: str, **kwargs: Any +def build_beta_agents_cancel_optimization_job_request( # pylint: disable=name-too-long + job_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 = "/evaluationtaxonomies/{name}" + _url = "/agent_optimization_jobs/{jobId}:cancel" path_format_arguments = { - "name": _SERIALIZER.url("name", name, "str"), + "jobId": _SERIALIZER.url("job_id", job_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -1150,73 +1321,76 @@ def build_beta_evaluation_taxonomies_delete_request( # pylint: disable=name-too # Construct parameters _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_evaluation_taxonomies_create_request( # pylint: disable=name-too-long - name: str, **kwargs: Any + +def build_beta_agents_delete_optimization_job_request( # pylint: disable=name-too-long + job_id: str, *, force: Optional[bool] = None, **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 = "/evaluationtaxonomies/{name}" + _url = "/agent_optimization_jobs/{jobId}" path_format_arguments = { - "name": _SERIALIZER.url("name", name, "str"), + "jobId": _SERIALIZER.url("job_id", job_id, "str"), } _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") - # 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="PUT", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) -def build_beta_evaluation_taxonomies_update_request( # pylint: disable=name-too-long - name: str, **kwargs: Any +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 {}) - 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 = "/evaluationtaxonomies/{name}" + _url = "/agent_optimization_jobs/{jobId}/candidates" path_format_arguments = { - "name": _SERIALIZER.url("name", name, "str"), + "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 - 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="PATCH", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_evaluators_list_versions_request( # pylint: disable=name-too-long - name: str, - *, - type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, - limit: Optional[int] = None, - **kwargs: Any +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 {}) @@ -1225,19 +1399,16 @@ def build_beta_evaluators_list_versions_request( # pylint: disable=name-too-lon accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/evaluators/{name}/versions" + _url = "/agent_optimization_jobs/{jobId}/candidates/{candidateId}" path_format_arguments = { - "name": _SERIALIZER.url("name", name, "str"), + "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") - if type is not None: - _params["type"] = _SERIALIZER.query("type", type, "str") - if limit is not None: - _params["limit"] = _SERIALIZER.query("limit", limit, "int") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") @@ -1245,11 +1416,8 @@ def build_beta_evaluators_list_versions_request( # pylint: disable=name-too-lon return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_evaluators_list_request( - *, - type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, - limit: Optional[int] = None, - **kwargs: Any +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 {}) @@ -1258,14 +1426,16 @@ def build_beta_evaluators_list_request( accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/evaluators" + _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") - if type is not None: - _params["type"] = _SERIALIZER.query("type", type, "str") - if limit is not None: - _params["limit"] = _SERIALIZER.query("limit", limit, "int") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") @@ -1273,8 +1443,8 @@ def build_beta_evaluators_list_request( return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_evaluators_get_version_request( # pylint: disable=name-too-long - name: str, version: str, **kwargs: Any +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 {}) @@ -1283,10 +1453,10 @@ def build_beta_evaluators_get_version_request( # pylint: disable=name-too-long accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/evaluators/{name}/versions/{version}" + _url = "/agent_optimization_jobs/{jobId}/candidates/{candidateId}/results" path_format_arguments = { - "name": _SERIALIZER.url("name", name, "str"), - "version": _SERIALIZER.url("version", version, "str"), + "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 @@ -1300,29 +1470,36 @@ def build_beta_evaluators_get_version_request( # pylint: disable=name-too-long return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_evaluators_delete_version_request( # pylint: disable=name-too-long - name: str, version: str, **kwargs: Any +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 = "/evaluators/{name}/versions/{version}" + _url = "/agent_optimization_jobs/{jobId}/candidates/{candidateId}/files" path_format_arguments = { - "name": _SERIALIZER.url("name", name, "str"), - "version": _SERIALIZER.url("version", version, "str"), + "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") - return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_evaluators_create_version_request( # pylint: disable=name-too-long - name: str, **kwargs: Any +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 {}) @@ -1332,9 +1509,10 @@ def build_beta_evaluators_create_version_request( # pylint: disable=name-too-lo accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/evaluators/{name}/versions" + _url = "/agent_optimization_jobs/{jobId}/candidates/{candidateId}:promote" path_format_arguments = { - "name": _SERIALIZER.url("name", name, "str"), + "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 @@ -1350,21 +1528,19 @@ def build_beta_evaluators_create_version_request( # pylint: disable=name-too-lo return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_evaluators_update_version_request( # pylint: disable=name-too-long - name: str, version: str, **kwargs: Any +def build_beta_evaluation_taxonomies_get_request( # pylint: disable=name-too-long + name: 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 = "/evaluators/{name}/versions/{version}" + _url = "/evaluationtaxonomies/{name}" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), - "version": _SERIALIZER.url("version", version, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -1373,116 +1549,73 @@ def build_beta_evaluators_update_version_request( # pylint: disable=name-too-lo _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="PATCH", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_insights_generate_request(**kwargs: Any) -> HttpRequest: +def build_beta_evaluation_taxonomies_list_request( # pylint: disable=name-too-long + *, input_name: Optional[str] = None, input_type: Optional[str] = None, **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 = "/insights" + _url = "/evaluationtaxonomies" # Construct parameters _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + if input_name is not None: + _params["inputName"] = _SERIALIZER.query("input_name", input_name, "str") + if input_type is not None: + _params["inputType"] = _SERIALIZER.query("input_type", input_type, "str") # Construct headers - if "Repeatability-Request-ID" not in _headers: - _headers["Repeatability-Request-ID"] = str(uuid.uuid4()) - if "Repeatability-First-Sent" not in _headers: - _headers["Repeatability-First-Sent"] = _SERIALIZER.serialize_data( - datetime.datetime.now(datetime.timezone.utc), "rfc-1123" - ) - 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) + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_insights_get_request( - insight_id: str, *, include_coordinates: Optional[bool] = None, **kwargs: Any +def build_beta_evaluation_taxonomies_delete_request( # pylint: disable=name-too-long + name: 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 = "/insights/{id}" + _url = "/evaluationtaxonomies/{name}" path_format_arguments = { - "id": _SERIALIZER.url("insight_id", insight_id, "str"), + "name": _SERIALIZER.url("name", name, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - if include_coordinates is not None: - _params["includeCoordinates"] = _SERIALIZER.query("include_coordinates", include_coordinates, "bool") _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) + return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) -def build_beta_insights_list_request( - *, - type: Optional[Union[str, _models.InsightType]] = None, - eval_id: Optional[str] = None, - run_id: Optional[str] = None, - agent_name: Optional[str] = None, - include_coordinates: Optional[bool] = None, - **kwargs: Any +def build_beta_evaluation_taxonomies_create_request( # pylint: disable=name-too-long + name: 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 = "/insights" - - # Construct parameters - if type is not None: - _params["type"] = _SERIALIZER.query("type", type, "str") - if eval_id is not None: - _params["evalId"] = _SERIALIZER.query("eval_id", eval_id, "str") - if run_id is not None: - _params["runId"] = _SERIALIZER.query("run_id", run_id, "str") - if agent_name is not None: - _params["agentName"] = _SERIALIZER.query("agent_name", agent_name, "str") - if include_coordinates is not None: - _params["includeCoordinates"] = _SERIALIZER.query("include_coordinates", include_coordinates, "bool") - _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_memory_stores_create_request(**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 = "/memory_stores" + _url = "/evaluationtaxonomies/{name}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") @@ -1492,10 +1625,12 @@ def build_beta_memory_stores_create_request(**kwargs: Any) -> HttpRequest: _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) + return HttpRequest(method="PUT", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_memory_stores_update_request(name: str, **kwargs: Any) -> HttpRequest: +def build_beta_evaluation_taxonomies_update_request( # pylint: disable=name-too-long + name: str, **kwargs: Any +) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1504,7 +1639,7 @@ def build_beta_memory_stores_update_request(name: str, **kwargs: Any) -> HttpReq accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/memory_stores/{name}" + _url = "/evaluationtaxonomies/{name}" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), } @@ -1519,10 +1654,16 @@ def build_beta_memory_stores_update_request(name: str, **kwargs: Any) -> HttpReq _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) + return HttpRequest(method="PATCH", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_memory_stores_get_request(name: str, **kwargs: Any) -> HttpRequest: +def build_beta_evaluators_list_versions_request( # pylint: disable=name-too-long + name: str, + *, + type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, + limit: Optional[int] = None, + **kwargs: Any +) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1530,7 +1671,7 @@ def build_beta_memory_stores_get_request(name: str, **kwargs: Any) -> HttpReques accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/memory_stores/{name}" + _url = "/evaluators/{name}/versions" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), } @@ -1539,6 +1680,10 @@ def build_beta_memory_stores_get_request(name: str, **kwargs: Any) -> HttpReques # Construct parameters _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + if type is not None: + _params["type"] = _SERIALIZER.query("type", type, "str") + if limit is not None: + _params["limit"] = _SERIALIZER.query("limit", limit, "int") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") @@ -1546,12 +1691,10 @@ def build_beta_memory_stores_get_request(name: str, **kwargs: Any) -> HttpReques return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_memory_stores_list_request( +def build_beta_evaluators_list_request( *, + type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, 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 {}) @@ -1561,18 +1704,14 @@ def build_beta_memory_stores_list_request( accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/memory_stores" + _url = "/evaluators" # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + if type is not None: + _params["type"] = _SERIALIZER.query("type", type, "str") 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") @@ -1580,7 +1719,9 @@ def build_beta_memory_stores_list_request( return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_memory_stores_delete_request(name: str, **kwargs: Any) -> HttpRequest: +def build_beta_evaluators_get_version_request( # pylint: disable=name-too-long + name: str, version: str, **kwargs: Any +) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1588,9 +1729,10 @@ def build_beta_memory_stores_delete_request(name: str, **kwargs: Any) -> HttpReq accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/memory_stores/{name}" + _url = "/evaluators/{name}/versions/{version}" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -1601,23 +1743,20 @@ def build_beta_memory_stores_delete_request(name: str, **kwargs: Any) -> HttpReq # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_memory_stores_search_memories_request( # pylint: disable=name-too-long - name: str, **kwargs: Any +def build_beta_evaluators_delete_version_request( # pylint: disable=name-too-long + name: str, version: 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 = "/memory_stores/{name}:search_memories" + _url = "/evaluators/{name}/versions/{version}" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -1625,15 +1764,10 @@ def build_beta_memory_stores_search_memories_request( # pylint: disable=name-to # 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) + return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) -def build_beta_memory_stores_update_memories_request( # pylint: disable=name-too-long +def build_beta_evaluators_create_version_request( # pylint: disable=name-too-long name: str, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) @@ -1644,7 +1778,7 @@ def build_beta_memory_stores_update_memories_request( # pylint: disable=name-to accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/memory_stores/{name}:update_memories" + _url = "/evaluators/{name}/versions" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), } @@ -1662,8 +1796,8 @@ def build_beta_memory_stores_update_memories_request( # pylint: disable=name-to return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_memory_stores_delete_scope_request( # pylint: disable=name-too-long - name: str, **kwargs: Any +def build_beta_evaluators_update_version_request( # pylint: disable=name-too-long + name: str, version: str, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1673,9 +1807,10 @@ def build_beta_memory_stores_delete_scope_request( # pylint: disable=name-too-l accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/memory_stores/{name}:delete_scope" + _url = "/evaluators/{name}/versions/{version}" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -1688,20 +1823,24 @@ def build_beta_memory_stores_delete_scope_request( # pylint: disable=name-too-l _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) + return HttpRequest(method="PATCH", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_red_teams_get_request(name: str, **kwargs: Any) -> HttpRequest: +def build_beta_evaluators_pending_upload_request( # pylint: disable=name-too-long + name: str, version: 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 = "/redTeams/runs/{name}" + _url = "/evaluators/{name}/versions/{version}/startPendingUpload" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -1710,31 +1849,46 @@ def build_beta_red_teams_get_request(name: str, **kwargs: Any) -> HttpRequest: _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="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_red_teams_list_request(**kwargs: Any) -> HttpRequest: +def build_beta_evaluators_get_credentials_request( # pylint: disable=name-too-long + name: str, version: 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 = "/redTeams/runs" + _url = "/evaluators/{name}/versions/{version}/credentials" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "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="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_red_teams_create_request(**kwargs: Any) -> HttpRequest: +def build_beta_evaluators_create_generation_job_request( # pylint: disable=name-too-long + *, operation_id: Optional[str] = None, **kwargs: Any +) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1743,12 +1897,14 @@ def build_beta_red_teams_create_request(**kwargs: Any) -> HttpRequest: accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/redTeams/runs:run" + _url = "/evaluator_generation_jobs" # Construct parameters _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") # Construct headers + if operation_id is not None: + _headers["Operation-Id"] = _SERIALIZER.header("operation_id", operation_id, "str") if content_type is not None: _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") @@ -1756,25 +1912,9 @@ def build_beta_red_teams_create_request(**kwargs: Any) -> HttpRequest: return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_schedules_delete_request(schedule_id: str, **kwargs: Any) -> HttpRequest: - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) - # Construct URL - _url = "/schedules/{id}" - path_format_arguments = { - "id": _SERIALIZER.url("schedule_id", schedule_id, "str"), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) - - -def build_beta_schedules_get_request(schedule_id: str, **kwargs: Any) -> HttpRequest: +def build_beta_evaluators_get_generation_job_request( # pylint: disable=name-too-long + job_id: str, **kwargs: Any +) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1782,9 +1922,9 @@ def build_beta_schedules_get_request(schedule_id: str, **kwargs: Any) -> HttpReq accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/schedules/{id}" + _url = "/evaluator_generation_jobs/{jobId}" path_format_arguments = { - "id": _SERIALIZER.url("schedule_id", schedule_id, "str"), + "jobId": _SERIALIZER.url("job_id", job_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -1798,8 +1938,13 @@ def build_beta_schedules_get_request(schedule_id: str, **kwargs: Any) -> HttpReq return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_schedules_list_request( - *, type: Optional[Union[str, _models.ScheduleTaskType]] = None, enabled: Optional[bool] = None, **kwargs: Any +def build_beta_evaluators_list_generation_jobs_request( # pylint: disable=name-too-long + *, + 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 {}) @@ -1808,14 +1953,18 @@ def build_beta_schedules_list_request( accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/schedules" + _url = "/evaluator_generation_jobs" # 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") - if type is not None: - _params["type"] = _SERIALIZER.query("type", type, "str") - if enabled is not None: - _params["enabled"] = _SERIALIZER.query("enabled", enabled, "bool") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") @@ -1823,20 +1972,19 @@ def build_beta_schedules_list_request( return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_schedules_create_or_update_request( # pylint: disable=name-too-long - schedule_id: str, **kwargs: Any +def build_beta_evaluators_cancel_generation_job_request( # pylint: disable=name-too-long + job_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 = "/schedules/{id}" + _url = "/evaluator_generation_jobs/{jobId}:cancel" path_format_arguments = { - "id": _SERIALIZER.url("schedule_id", schedule_id, "str"), + "jobId": _SERIALIZER.url("job_id", job_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -1845,25 +1993,21 @@ def build_beta_schedules_create_or_update_request( # pylint: disable=name-too-l _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="PUT", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_schedules_get_run_request(schedule_id: str, run_id: str, **kwargs: Any) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) +def build_beta_evaluators_delete_generation_job_request( # pylint: disable=name-too-long + job_id: str, **kwargs: Any +) -> HttpRequest: _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 = "/schedules/{schedule_id}/runs/{run_id}" + _url = "/evaluator_generation_jobs/{jobId}" path_format_arguments = { - "schedule_id": _SERIALIZER.url("schedule_id", schedule_id, "str"), - "run_id": _SERIALIZER.url("run_id", run_id, "str"), + "jobId": _SERIALIZER.url("job_id", job_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -1871,18 +2015,39 @@ def build_beta_schedules_get_run_request(schedule_id: str, run_id: str, **kwargs # Construct parameters _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) +def build_beta_insights_generate_request(**kwargs: Any) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) -def build_beta_schedules_list_runs_request( - schedule_id: str, - *, - type: Optional[Union[str, _models.ScheduleTaskType]] = None, - enabled: Optional[bool] = None, - **kwargs: Any + 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 = "/insights" + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if "Repeatability-Request-ID" not in _headers: + _headers["Repeatability-Request-ID"] = str(uuid.uuid4()) + if "Repeatability-First-Sent" not in _headers: + _headers["Repeatability-First-Sent"] = _SERIALIZER.serialize_data( + datetime.datetime.now(datetime.timezone.utc), "rfc-1123" + ) + 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_insights_get_request( + insight_id: str, *, include_coordinates: Optional[bool] = None, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1891,19 +2056,54 @@ def build_beta_schedules_list_runs_request( accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/schedules/{id}/runs" + _url = "/insights/{id}" path_format_arguments = { - "id": _SERIALIZER.url("schedule_id", schedule_id, "str"), + "id": _SERIALIZER.url("insight_id", insight_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters + if include_coordinates is not None: + _params["includeCoordinates"] = _SERIALIZER.query("include_coordinates", include_coordinates, "bool") _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_insights_list_request( + *, + type: Optional[Union[str, _models.InsightType]] = None, + eval_id: Optional[str] = None, + run_id: Optional[str] = None, + agent_name: Optional[str] = None, + include_coordinates: Optional[bool] = 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 = "/insights" + + # Construct parameters if type is not None: _params["type"] = _SERIALIZER.query("type", type, "str") - if enabled is not None: - _params["enabled"] = _SERIALIZER.query("enabled", enabled, "bool") + if eval_id is not None: + _params["evalId"] = _SERIALIZER.query("eval_id", eval_id, "str") + if run_id is not None: + _params["runId"] = _SERIALIZER.query("run_id", run_id, "str") + if agent_name is not None: + _params["agentName"] = _SERIALIZER.query("agent_name", agent_name, "str") + if include_coordinates is not None: + _params["includeCoordinates"] = _SERIALIZER.query("include_coordinates", include_coordinates, "bool") + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") @@ -1911,9 +2111,7 @@ def build_beta_schedules_list_runs_request( return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_toolboxes_create_version_request( # pylint: disable=name-too-long - name: str, **kwargs: Any -) -> HttpRequest: +def build_beta_memory_stores_create_request(**kwargs: Any) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1922,12 +2120,7 @@ def build_beta_toolboxes_create_version_request( # pylint: disable=name-too-lon accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/toolboxes/{name}/versions" - path_format_arguments = { - "name": _SERIALIZER.url("name", name, "str"), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore + _url = "/memory_stores" # Construct parameters _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") @@ -1940,15 +2133,16 @@ def build_beta_toolboxes_create_version_request( # pylint: disable=name-too-lon return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_toolboxes_get_request(name: str, **kwargs: Any) -> HttpRequest: +def build_beta_memory_stores_update_request(name: 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 = "/toolboxes/{name}" + _url = "/memory_stores/{name}" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), } @@ -1959,19 +2153,14 @@ def build_beta_toolboxes_get_request(name: str, **kwargs: Any) -> HttpRequest: _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="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_toolboxes_list_request( - *, - limit: Optional[int] = None, - order: Optional[Union[str, _models.PageOrder]] = None, - after: Optional[str] = None, - before: Optional[str] = None, - **kwargs: Any -) -> HttpRequest: +def build_beta_memory_stores_get_request(name: str, **kwargs: Any) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1979,17 +2168,14 @@ def build_beta_toolboxes_list_request( accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/toolboxes" + _url = "/memory_stores/{name}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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 @@ -1998,8 +2184,7 @@ def build_beta_toolboxes_list_request( return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_toolboxes_list_versions_request( # pylint: disable=name-too-long - name: str, +def build_beta_memory_stores_list_request( *, limit: Optional[int] = None, order: Optional[Union[str, _models.PageOrder]] = None, @@ -2014,12 +2199,7 @@ def build_beta_toolboxes_list_versions_request( # pylint: disable=name-too-long accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/toolboxes/{name}/versions" - path_format_arguments = { - "name": _SERIALIZER.url("name", name, "str"), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore + _url = "/memory_stores" # Construct parameters if limit is not None: @@ -2038,7 +2218,7 @@ def build_beta_toolboxes_list_versions_request( # pylint: disable=name-too-long return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_toolboxes_get_version_request(name: str, version: str, **kwargs: Any) -> HttpRequest: +def build_beta_memory_stores_delete_request(name: str, **kwargs: Any) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -2046,10 +2226,9 @@ def build_beta_toolboxes_get_version_request(name: str, version: str, **kwargs: accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/toolboxes/{name}/versions/{version}" + _url = "/memory_stores/{name}" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), - "version": _SERIALIZER.url("version", version, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -2060,10 +2239,12 @@ def build_beta_toolboxes_get_version_request(name: str, version: str, **kwargs: # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_toolboxes_update_request(name: str, **kwargs: Any) -> HttpRequest: +def build_beta_memory_stores_search_memories_request( # pylint: disable=name-too-long + name: str, **kwargs: Any +) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -2072,7 +2253,7 @@ def build_beta_toolboxes_update_request(name: str, **kwargs: Any) -> HttpRequest accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/toolboxes/{name}" + _url = "/memory_stores/{name}:search_memories" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), } @@ -2087,15 +2268,21 @@ def build_beta_toolboxes_update_request(name: str, **kwargs: Any) -> HttpRequest _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="PATCH", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_toolboxes_delete_request(name: str, **kwargs: Any) -> HttpRequest: +def build_beta_memory_stores_update_memories_request( # pylint: disable=name-too-long + name: 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 = "/toolboxes/{name}" + _url = "/memory_stores/{name}:update_memories" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), } @@ -2105,20 +2292,28 @@ def build_beta_toolboxes_delete_request(name: str, **kwargs: Any) -> HttpRequest # Construct parameters _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) + # 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_toolboxes_delete_version_request( # pylint: disable=name-too-long - name: str, version: str, **kwargs: Any + +def build_beta_memory_stores_delete_scope_request( # pylint: disable=name-too-long + name: 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 = "/toolboxes/{name}/versions/{version}" + _url = "/memory_stores/{name}:delete_scope" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), - "version": _SERIALIZER.url("version", version, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -2126,23 +2321,6 @@ def build_beta_toolboxes_delete_version_request( # pylint: disable=name-too-lon # Construct parameters _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) - - -def build_beta_skills_create_request(**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 = "/skills" - - # 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") @@ -2151,38 +2329,50 @@ def build_beta_skills_create_request(**kwargs: Any) -> HttpRequest: return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_skills_create_from_package_request(**kwargs: Any) -> HttpRequest: # pylint: disable=name-too-long +def build_beta_memory_stores_create_memory_request( # pylint: disable=name-too-long + name: str, **kwargs: Any +) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - content_type: str = kwargs.pop("content_type") + 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 = "/skills:import" + _url = "/memory_stores/{name}/items" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + 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_skills_get_request(name: str, **kwargs: Any) -> HttpRequest: +def build_beta_memory_stores_update_memory_request( # pylint: disable=name-too-long + name: str, memory_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 = "/skills/{name}" + _url = "/memory_stores/{name}/items/{memory_id}" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), + "memory_id": _SERIALIZER.url("memory_id", memory_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -2191,22 +2381,27 @@ def build_beta_skills_get_request(name: str, **kwargs: Any) -> HttpRequest: _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="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_skills_download_request(name: str, **kwargs: Any) -> HttpRequest: +def build_beta_memory_stores_get_memory_request( # pylint: disable=name-too-long + name: str, memory_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/zip") + accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/skills/{name}:download" + _url = "/memory_stores/{name}/items/{memory_id}" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), + "memory_id": _SERIALIZER.url("memory_id", memory_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -2220,8 +2415,10 @@ def build_beta_skills_download_request(name: str, **kwargs: Any) -> HttpRequest: return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_skills_list_request( +def build_beta_memory_stores_list_memories_request( # pylint: disable=name-too-long + name: str, *, + kind: Optional[Union[str, _models.MemoryItemKind]] = None, limit: Optional[int] = None, order: Optional[Union[str, _models.PageOrder]] = None, after: Optional[str] = None, @@ -2231,13 +2428,21 @@ def build_beta_skills_list_request( _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 = "/skills" + _url = "/memory_stores/{name}/items:list" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters + if kind is not None: + _params["kind"] = _SERIALIZER.query("kind", kind, "str") if limit is not None: _params["limit"] = _SERIALIZER.query("limit", limit, "int") if order is not None: @@ -2249,23 +2454,27 @@ def build_beta_skills_list_request( _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="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_skills_update_request(name: str, **kwargs: Any) -> HttpRequest: +def build_beta_memory_stores_delete_memory_request( # pylint: disable=name-too-long + name: str, memory_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 = "/skills/{name}" + _url = "/memory_stores/{name}/items/{memory_id}" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), + "memory_id": _SERIALIZER.url("memory_id", memory_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -2274,14 +2483,12 @@ def build_beta_skills_update_request(name: str, **kwargs: Any) -> HttpRequest: _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) + return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) -def build_beta_skills_delete_request(name: str, **kwargs: Any) -> HttpRequest: +def build_beta_models_list_versions_request(name: str, **kwargs: Any) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -2289,7 +2496,7 @@ def build_beta_skills_delete_request(name: str, **kwargs: Any) -> HttpRequest: accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/skills/{name}" + _url = "/models/{name}/versions" path_format_arguments = { "name": _SERIALIZER.url("name", name, "str"), } @@ -2302,409 +2509,6296 @@ def build_beta_skills_delete_request(name: str, **kwargs: Any) -> HttpRequest: # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) -class BetaOperations: # pylint: disable=too-many-instance-attributes - """ - .. warning:: - **DO NOT** instantiate this class directly. +def build_beta_models_list_request(**kwargs: Any) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`beta` attribute. - """ + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + accept = _headers.pop("Accept", "application/json") - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + # Construct URL + _url = "/models" - self.agents = BetaAgentsOperations(self._client, self._config, self._serialize, self._deserialize) - self.evaluation_taxonomies = BetaEvaluationTaxonomiesOperations( - self._client, self._config, self._serialize, self._deserialize - ) - self.evaluators = BetaEvaluatorsOperations(self._client, self._config, self._serialize, self._deserialize) - self.insights = BetaInsightsOperations(self._client, self._config, self._serialize, self._deserialize) - self.memory_stores = BetaMemoryStoresOperations(self._client, self._config, self._serialize, self._deserialize) - self.red_teams = BetaRedTeamsOperations(self._client, self._config, self._serialize, self._deserialize) - self.schedules = BetaSchedulesOperations(self._client, self._config, self._serialize, self._deserialize) - self.toolboxes = BetaToolboxesOperations(self._client, self._config, self._serialize, self._deserialize) - self.skills = BetaSkillsOperations(self._client, self._config, self._serialize, self._deserialize) + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") -class AgentsOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`agents` attribute. - """ - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") +def build_beta_models_get_request(name: str, version: str, **kwargs: Any) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - @distributed_trace - def get(self, agent_name: str, **kwargs: Any) -> _models.AgentDetails: - """Retrieves the agent. + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + accept = _headers.pop("Accept", "application/json") - :param agent_name: The name of the agent to retrieve. Required. - :type agent_name: str - :return: AgentDetails. The AgentDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentDetails - :raises ~azure.core.exceptions.HttpResponseError: - """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) + # Construct URL + _url = "/models/{name}/versions/{version}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "str"), + } - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} + _url: str = _url.format(**path_format_arguments) # type: ignore - cls: ClsType[_models.AgentDetails] = kwargs.pop("cls", None) + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - _request = build_agents_get_request( - agent_name=agent_name, - 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) + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - _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 - ) + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - response = pipeline_response.http_response - 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, - ) - raise HttpResponseError(response=response, model=error) +def build_beta_models_delete_request(name: str, version: str, **kwargs: Any) -> HttpRequest: + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.AgentDetails, response.json()) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + # Construct URL + _url = "/models/{name}/versions/{version}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "str"), + } - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + _url: str = _url.format(**path_format_arguments) # type: ignore - return deserialized # type: ignore + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - @distributed_trace - def delete(self, agent_name: str, **kwargs: Any) -> _models.DeleteAgentResponse: - """Deletes an agent. + return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) - :param agent_name: The name of the agent to delete. Required. - :type agent_name: str - :return: DeleteAgentResponse. The DeleteAgentResponse is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.DeleteAgentResponse - :raises ~azure.core.exceptions.HttpResponseError: - """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} +def build_beta_models_update_request(name: str, version: str, **kwargs: Any) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - cls: ClsType[_models.DeleteAgentResponse] = kwargs.pop("cls", None) + 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") - _request = build_agents_delete_request( - agent_name=agent_name, - 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) + # Construct URL + _url = "/models/{name}/versions/{version}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "str"), + } - _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 - ) + _url: str = _url.format(**path_format_arguments) # type: ignore - response = pipeline_response.http_response + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - 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, - ) - raise HttpResponseError(response=response, model=error) + # 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") - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.DeleteAgentResponse, response.json()) + return HttpRequest(method="PATCH", url=_url, params=_params, headers=_headers, **kwargs) - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore - return deserialized # type: ignore +def build_beta_models_pending_create_version_request( # pylint: disable=name-too-long + name: str, version: str, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - @distributed_trace - def list( - self, - *, - kind: Optional[Union[str, _models.AgentKind]] = None, - limit: Optional[int] = None, - order: Optional[Union[str, _models.PageOrder]] = None, - before: Optional[str] = None, - **kwargs: Any - ) -> ItemPaged["_models.AgentDetails"]: - """Returns the list of all agents. + 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") - :keyword kind: Filter agents by kind. If not provided, all agents are returned. Known values - are: "prompt", "hosted", and "workflow". Default value is None. - :paramtype kind: str or ~azure.ai.projects.models.AgentKind - :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the - default is 20. Default value is None. - :paramtype limit: int - :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for - 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 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: An iterator like instance of AgentDetails - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.AgentDetails] - :raises ~azure.core.exceptions.HttpResponseError: - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} + # Construct URL + _url = "/models/{name}/versions/{version}/createAsync" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "str"), + } - cls: ClsType[List[_models.AgentDetails]] = kwargs.pop("cls", None) + _url: str = _url.format(**path_format_arguments) # type: ignore - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - def prepare_request(_continuation_token=None): + # 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") - _request = build_agents_list_request( - kind=kind, - 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 HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_models_pending_upload_request(name: str, version: 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 = "/models/{name}/versions/{version}/startPendingUpload" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "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_models_get_credentials_request( # pylint: disable=name-too-long + name: str, version: 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 = "/models/{name}/versions/{version}/credentials" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "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_red_teams_get_request(name: 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 = "/redTeams/runs/{name}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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_red_teams_list_request(**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 = "/redTeams/runs" + + # 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_red_teams_create_request(**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 = "/redTeams/runs:run" + + # 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_routines_create_or_update_request( # pylint: disable=name-too-long + routine_name: 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 = "/routines/{routine_name}" + path_format_arguments = { + "routine_name": _SERIALIZER.url("routine_name", routine_name, "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="PUT", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_routines_get_request(routine_name: 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 = "/routines/{routine_name}" + path_format_arguments = { + "routine_name": _SERIALIZER.url("routine_name", routine_name, "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_routines_enable_request(routine_name: 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 = "/routines/{routine_name}:enable" + path_format_arguments = { + "routine_name": _SERIALIZER.url("routine_name", routine_name, "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="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_routines_disable_request(routine_name: 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 = "/routines/{routine_name}:disable" + path_format_arguments = { + "routine_name": _SERIALIZER.url("routine_name", routine_name, "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="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_routines_list_request( + *, + limit: Optional[int] = None, + after: Optional[str] = None, + before: Optional[str] = None, + order: 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 = "/routines" + + # Construct parameters + if limit is not None: + _params["limit"] = _SERIALIZER.query("limit", limit, "int") + if after is not None: + _params["after"] = _SERIALIZER.query("after", after, "str") + if before is not None: + _params["before"] = _SERIALIZER.query("before", before, "str") + if order is not None: + _params["order"] = _SERIALIZER.query("order", order, "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_routines_delete_request(routine_name: str, **kwargs: Any) -> HttpRequest: + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + # Construct URL + _url = "/routines/{routine_name}" + path_format_arguments = { + "routine_name": _SERIALIZER.url("routine_name", routine_name, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) + + +def build_beta_routines_list_runs_request( + routine_name: str, + *, + filter: Optional[str] = None, + limit: Optional[int] = None, + after: Optional[str] = None, + before: Optional[str] = None, + order: 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 = "/routines/{routine_name}/runs" + path_format_arguments = { + "routine_name": _SERIALIZER.url("routine_name", routine_name, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + if filter is not None: + _params["filter"] = _SERIALIZER.query("filter", filter, "str") + if limit is not None: + _params["limit"] = _SERIALIZER.query("limit", limit, "int") + if after is not None: + _params["after"] = _SERIALIZER.query("after", after, "str") + if before is not None: + _params["before"] = _SERIALIZER.query("before", before, "str") + if order is not None: + _params["order"] = _SERIALIZER.query("order", order, "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_routines_dispatch_request(routine_name: 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 = "/routines/{routine_name}:dispatch_async" + path_format_arguments = { + "routine_name": _SERIALIZER.url("routine_name", routine_name, "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_schedules_delete_request(schedule_id: str, **kwargs: Any) -> HttpRequest: + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + # Construct URL + _url = "/schedules/{id}" + path_format_arguments = { + "id": _SERIALIZER.url("schedule_id", schedule_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) + + +def build_beta_schedules_get_request(schedule_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 = "/schedules/{id}" + path_format_arguments = { + "id": _SERIALIZER.url("schedule_id", schedule_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_schedules_list_request( + *, type: Optional[Union[str, _models.ScheduleTaskType]] = None, enabled: Optional[bool] = 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 = "/schedules" + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + if type is not None: + _params["type"] = _SERIALIZER.query("type", type, "str") + if enabled is not None: + _params["enabled"] = _SERIALIZER.query("enabled", enabled, "bool") + + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_schedules_create_or_update_request( # pylint: disable=name-too-long + schedule_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 = "/schedules/{id}" + path_format_arguments = { + "id": _SERIALIZER.url("schedule_id", schedule_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="PUT", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_schedules_get_run_request(schedule_id: str, run_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 = "/schedules/{schedule_id}/runs/{run_id}" + path_format_arguments = { + "schedule_id": _SERIALIZER.url("schedule_id", schedule_id, "str"), + "run_id": _SERIALIZER.url("run_id", run_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_schedules_list_runs_request( + schedule_id: str, + *, + type: Optional[Union[str, _models.ScheduleTaskType]] = None, + enabled: Optional[bool] = 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 = "/schedules/{id}/runs" + path_format_arguments = { + "id": _SERIALIZER.url("schedule_id", schedule_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + if type is not None: + _params["type"] = _SERIALIZER.query("type", type, "str") + if enabled is not None: + _params["enabled"] = _SERIALIZER.query("enabled", enabled, "bool") + + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_toolboxes_create_version_request( # pylint: disable=name-too-long + name: 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 = "/toolboxes/{name}/versions" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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_toolboxes_get_request(name: 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 = "/toolboxes/{name}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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_toolboxes_list_request( + *, + 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 = "/toolboxes" + + # 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_toolboxes_list_versions_request( # pylint: disable=name-too-long + name: 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 = "/toolboxes/{name}/versions" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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_toolboxes_get_version_request(name: str, version: 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 = "/toolboxes/{name}/versions/{version}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "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_toolboxes_update_request(name: 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 = "/toolboxes/{name}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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="PATCH", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_toolboxes_delete_request(name: str, **kwargs: Any) -> HttpRequest: + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + # Construct URL + _url = "/toolboxes/{name}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) + + +def build_beta_toolboxes_delete_version_request( # pylint: disable=name-too-long + name: str, version: str, **kwargs: Any +) -> HttpRequest: + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + # Construct URL + _url = "/toolboxes/{name}/versions/{version}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) + + +def build_beta_skills_get_request(name: 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 = "/skills/{name}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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_skills_list_request( + *, + 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 = "/skills" + + # 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_skills_update_request(name: 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 = "/skills/{name}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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_skills_delete_request(name: 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 = "/skills/{name}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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="DELETE", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_skills_create_request(name: 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 = "/skills/{name}/versions" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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_skills_create_from_files_request( # pylint: disable=name-too-long + name: 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 = "/skills/{name}/versions" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_skills_list_versions_request( + name: 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 = "/skills/{name}/versions" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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_skills_get_version_request(name: str, version: 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 = "/skills/{name}/versions/{version}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "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_skills_download_request(name: 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/zip") + + # Construct URL + _url = "/skills/{name}/content" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "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_skills_download_version_request( # pylint: disable=name-too-long + name: str, version: 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/zip") + + # Construct URL + _url = "/skills/{name}/versions/{version}/content" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "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_skills_delete_version_request(name: str, version: 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 = "/skills/{name}/versions/{version}" + path_format_arguments = { + "name": _SERIALIZER.url("name", name, "str"), + "version": _SERIALIZER.url("version", version, "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="DELETE", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_datasets_get_generation_job_request( # pylint: disable=name-too-long + job_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 = "/data_generation_jobs/{jobId}" + path_format_arguments = { + "jobId": _SERIALIZER.url("job_id", job_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_datasets_list_generation_jobs_request( # pylint: disable=name-too-long + *, + 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 = "/data_generation_jobs" + + # 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_datasets_create_generation_job_request( # pylint: disable=name-too-long + *, operation_id: Optional[str] = None, **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 = "/data_generation_jobs" + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if operation_id is not None: + _headers["Operation-Id"] = _SERIALIZER.header("operation_id", operation_id, "str") + 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_datasets_cancel_generation_job_request( # pylint: disable=name-too-long + job_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 = "/data_generation_jobs/{jobId}:cancel" + path_format_arguments = { + "jobId": _SERIALIZER.url("job_id", job_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="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_datasets_delete_generation_job_request( # pylint: disable=name-too-long + job_id: str, **kwargs: Any +) -> HttpRequest: + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + # Construct URL + _url = "/data_generation_jobs/{jobId}" + path_format_arguments = { + "jobId": _SERIALIZER.url("job_id", job_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) + + +class BetaOperations: # pylint: disable=too-many-instance-attributes + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`beta` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + self.agents = BetaAgentsOperations(self._client, self._config, self._serialize, self._deserialize) + self.evaluation_taxonomies = BetaEvaluationTaxonomiesOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.evaluators = BetaEvaluatorsOperations(self._client, self._config, self._serialize, self._deserialize) + self.insights = BetaInsightsOperations(self._client, self._config, self._serialize, self._deserialize) + self.memory_stores = BetaMemoryStoresOperations(self._client, self._config, self._serialize, self._deserialize) + self.models = BetaModelsOperations(self._client, self._config, self._serialize, self._deserialize) + self.red_teams = BetaRedTeamsOperations(self._client, self._config, self._serialize, self._deserialize) + self.routines = BetaRoutinesOperations(self._client, self._config, self._serialize, self._deserialize) + self.schedules = BetaSchedulesOperations(self._client, self._config, self._serialize, self._deserialize) + self.toolboxes = BetaToolboxesOperations(self._client, self._config, self._serialize, self._deserialize) + self.skills = BetaSkillsOperations(self._client, self._config, self._serialize, self._deserialize) + self.datasets = BetaDatasetsOperations(self._client, self._config, self._serialize, self._deserialize) + + +class AgentsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`agents` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @distributed_trace + def get(self, agent_name: str, **kwargs: Any) -> _models.AgentDetails: + """Retrieves the agent. + + :param agent_name: The name of the agent to retrieve. Required. + :type agent_name: str + :return: AgentDetails. The AgentDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.AgentDetails] = kwargs.pop("cls", None) + + _request = build_agents_get_request( + agent_name=agent_name, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @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 + 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. + + :param agent_name: The name of the agent to delete. Required. + :type agent_name: str + :keyword force: For Hosted Agents, if true, force-deletes the agent even if its versions have + active sessions, cascading deletion to all associated sessions. Defaults to ``false``. This + value is not relevant for other Agent types. Default value is None. + :paramtype force: bool + :return: DeleteAgentResponse. The DeleteAgentResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DeleteAgentResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.DeleteAgentResponse] = kwargs.pop("cls", None) + + _request = build_agents_delete_request( + agent_name=agent_name, + force=force, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DeleteAgentResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list( + self, + *, + kind: Optional[Union[str, _models.AgentKind]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_models.AgentDetails"]: + """Returns the list of all agents. + + :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. + :paramtype kind: str or ~azure.ai.projects.models.AgentKind + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of AgentDetails + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.AgentDetails] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.AgentDetails]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_agents_list_request( + kind=kind, + 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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.AgentDetails], + 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) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @overload + def create_version( + self, + agent_name: str, + *, + definition: _models.AgentDefinition, + content_type: str = "application/json", + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + **kwargs: Any + ) -> _models.AgentVersionDetails: + """Create a new agent version. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :keyword definition: The agent definition. This can be a workflow, hosted agent, or a simple + agent definition. Required. + :paramtype definition: ~azure.ai.projects.models.AgentDefinition + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be + useful for storing additional information about the object in a structured + format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings + with a maximum length of 512 characters. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.projects.models.AgentBlueprintReference + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_version( + self, agent_name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.AgentVersionDetails: + """Create a new agent version. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_version( + self, agent_name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.AgentVersionDetails: + """Create a new agent version. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def create_version( + self, + agent_name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + definition: _models.AgentDefinition = _Unset, + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + **kwargs: Any + ) -> _models.AgentVersionDetails: + """Create a new agent version. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword definition: The agent definition. This can be a workflow, hosted agent, or a simple + agent definition. Required. + :paramtype definition: ~azure.ai.projects.models.AgentDefinition + :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be + useful for storing additional information about the object in a structured + format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings + with a maximum length of 512 characters. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.projects.models.AgentBlueprintReference + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.AgentVersionDetails] = kwargs.pop("cls", None) + + if body is _Unset: + if definition is _Unset: + raise TypeError("missing required argument: definition") + body = { + "blueprint_reference": blueprint_reference, + "definition": definition, + "description": description, + "metadata": metadata, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_agents_create_version_request( + agent_name=agent_name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentVersionDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + def create_version_from_manifest( + self, + agent_name: str, + *, + manifest_id: str, + parameter_values: dict[str, Any], + content_type: str = "application/json", + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + **kwargs: Any + ) -> _models.AgentVersionDetails: + """Create a new agent version from a manifest. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :keyword manifest_id: The manifest ID to import the agent version from. Required. + :paramtype manifest_id: str + :keyword parameter_values: The inputs to the manifest that will result in a fully materialized + Agent. Required. + :paramtype parameter_values: dict[str, any] + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be + useful for storing additional information about the object in a structured + format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings + with a maximum length of 512 characters. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def create_version_from_manifest( + self, + agent_name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + manifest_id: str = _Unset, + parameter_values: dict[str, Any] = _Unset, + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + **kwargs: Any + ) -> _models.AgentVersionDetails: + """Create a new agent version from a manifest. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword manifest_id: The manifest ID to import the agent version from. Required. + :paramtype manifest_id: str + :keyword parameter_values: The inputs to the manifest that will result in a fully materialized + Agent. Required. + :paramtype parameter_values: dict[str, any] + :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be + useful for storing additional information about the object in a structured + format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings + with a maximum length of 512 characters. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.AgentVersionDetails] = kwargs.pop("cls", None) + + if body is _Unset: + if manifest_id is _Unset: + raise TypeError("missing required argument: manifest_id") + if parameter_values is _Unset: + raise TypeError("missing required argument: parameter_values") + body = { + "description": description, + "manifest_id": manifest_id, + "metadata": metadata, + "parameter_values": parameter_values, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_agents_create_version_from_manifest_request( + agent_name=agent_name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentVersionDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def get_version(self, agent_name: str, agent_version: str, **kwargs: Any) -> _models.AgentVersionDetails: + """Retrieves a specific version of an agent. + + :param agent_name: The name of the agent to retrieve. Required. + :type agent_name: str + :param agent_version: The version of the agent to retrieve. Required. + :type agent_version: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.AgentVersionDetails] = kwargs.pop("cls", None) + + _request = build_agents_get_version_request( + agent_name=agent_name, + agent_version=agent_version, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentVersionDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + 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, + 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. + + :param agent_name: The name of the agent to delete. Required. + :type agent_name: str + :param agent_version: The version of the agent to delete. Required. + :type agent_version: str + :keyword force: For Hosted Agents, if true, force-deletes the version even if it has active + sessions, cascading deletion to all associated sessions. Defaults to ``false``. This value is + not relevant for other Agent types. Default value is None. + :paramtype force: bool + :return: DeleteAgentVersionResponse. The DeleteAgentVersionResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.DeleteAgentVersionResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.DeleteAgentVersionResponse] = kwargs.pop("cls", None) + + _request = build_agents_delete_version_request( + agent_name=agent_name, + agent_version=agent_version, + force=force, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DeleteAgentVersionResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_versions( + self, + agent_name: str, + *, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_models.AgentVersionDetails"]: + """Returns the list of versions of an agent. + + :param agent_name: The name of the agent to retrieve versions for. Required. + :type agent_name: str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of AgentVersionDetails + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.AgentVersionDetails] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.AgentVersionDetails]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_agents_list_versions_request( + agent_name=agent_name, + 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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.AgentVersionDetails], + 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) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + +class EvaluationRulesOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`evaluation_rules` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @distributed_trace + def get(self, id: str, **kwargs: Any) -> _models.EvaluationRule: + """Get an evaluation rule. + + :param id: Unique identifier for the evaluation rule. Required. + :type id: str + :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationRule + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.EvaluationRule] = kwargs.pop("cls", None) + + _request = build_evaluation_rules_get_request( + id=id, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.EvaluationRule, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def delete(self, id: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements + """Delete an evaluation rule. + + :param id: Unique identifier for the evaluation rule. Required. + :type id: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_evaluation_rules_delete_request( + id=id, + 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) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if cls: + return cls(pipeline_response, None, {}) # type: ignore + + @overload + def create_or_update( + self, id: str, evaluation_rule: _models.EvaluationRule, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationRule: + """Create or update an evaluation rule. + + :param id: Unique identifier for the evaluation rule. Required. + :type id: str + :param evaluation_rule: Evaluation rule resource. Required. + :type evaluation_rule: ~azure.ai.projects.models.EvaluationRule + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationRule + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_or_update( + self, id: str, evaluation_rule: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationRule: + """Create or update an evaluation rule. + + :param id: Unique identifier for the evaluation rule. Required. + :type id: str + :param evaluation_rule: Evaluation rule resource. Required. + :type evaluation_rule: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationRule + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_or_update( + self, id: str, evaluation_rule: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationRule: + """Create or update an evaluation rule. + + :param id: Unique identifier for the evaluation rule. Required. + :type id: str + :param evaluation_rule: Evaluation rule resource. Required. + :type evaluation_rule: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationRule + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def create_or_update( + self, id: str, evaluation_rule: Union[_models.EvaluationRule, JSON, IO[bytes]], **kwargs: Any + ) -> _models.EvaluationRule: + """Create or update an evaluation rule. + + :param id: Unique identifier for the evaluation rule. Required. + :type id: str + :param evaluation_rule: Evaluation rule resource. Is one of the following types: + EvaluationRule, JSON, IO[bytes] Required. + :type evaluation_rule: ~azure.ai.projects.models.EvaluationRule or JSON or IO[bytes] + :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationRule + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.EvaluationRule] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(evaluation_rule, (IOBase, bytes)): + _content = evaluation_rule + else: + _content = json.dumps(evaluation_rule, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_evaluation_rules_create_or_update_request( + id=id, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 201]: + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.EvaluationRule, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list( + self, + *, + action_type: Optional[Union[str, _models.EvaluationRuleActionType]] = None, + agent_name: Optional[str] = None, + enabled: Optional[bool] = None, + **kwargs: Any + ) -> ItemPaged["_models.EvaluationRule"]: + """List all evaluation rules. + + :keyword action_type: Filter by the type of evaluation rule. Known values are: + "continuousEvaluation" and "humanEvaluationPreview". Default value is None. + :paramtype action_type: str or ~azure.ai.projects.models.EvaluationRuleActionType + :keyword agent_name: Filter by the agent name. Default value is None. + :paramtype agent_name: str + :keyword enabled: Filter by the enabled status. Default value is None. + :paramtype enabled: bool + :return: An iterator like instance of EvaluationRule + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.EvaluationRule] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.EvaluationRule]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_evaluation_rules_list_request( + action_type=action_type, + agent_name=agent_name, + enabled=enabled, + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.EvaluationRule], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + +class ConnectionsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`connections` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @distributed_trace + def _get(self, name: str, **kwargs: Any) -> _models.Connection: + """Get a connection by name, without populating connection credentials. + + :param name: The friendly name of the connection, provided by the user. Required. + :type name: str + :return: Connection. The Connection is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Connection + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.Connection] = kwargs.pop("cls", None) + + _request = build_connections_get_request( + name=name, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["x-ms-client-request-id"] = self._deserialize( + "str", response.headers.get("x-ms-client-request-id") + ) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Connection, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def _get_with_credentials(self, name: str, **kwargs: Any) -> _models.Connection: + """Get a connection by name, with its connection credentials. + + :param name: The friendly name of the connection, provided by the user. Required. + :type name: str + :return: Connection. The Connection is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Connection + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.Connection] = kwargs.pop("cls", None) + + _request = build_connections_get_with_credentials_request( + name=name, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["x-ms-client-request-id"] = self._deserialize( + "str", response.headers.get("x-ms-client-request-id") + ) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Connection, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list( + self, + *, + connection_type: Optional[Union[str, _models.ConnectionType]] = None, + default_connection: Optional[bool] = None, + **kwargs: Any + ) -> ItemPaged["_models.Connection"]: + """List all connections in the project, without populating connection credentials. + + :keyword connection_type: List 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 + None. + :paramtype default_connection: bool + :return: An iterator like instance of Connection + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Connection] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.Connection]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_connections_list_request( + connection_type=connection_type, + default_connection=default_connection, + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.Connection], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + +class DatasetsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`datasets` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @distributed_trace + def list_versions(self, name: str, **kwargs: Any) -> ItemPaged["_models.DatasetVersion"]: + """List all versions of the given DatasetVersion. + + :param name: The name of the resource. Required. + :type name: str + :return: An iterator like instance of DatasetVersion + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.DatasetVersion] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.DatasetVersion]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_datasets_list_versions_request( + name=name, + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.DatasetVersion], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def list(self, **kwargs: Any) -> ItemPaged["_models.DatasetVersion"]: + """List the latest version of each DatasetVersion. + + :return: An iterator like instance of DatasetVersion + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.DatasetVersion] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.DatasetVersion]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_datasets_list_request( + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.DatasetVersion], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @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 + DatasetVersion does not exist. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the DatasetVersion to retrieve. Required. + :type version: str + :return: DatasetVersion. The DatasetVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.DatasetVersion] = kwargs.pop("cls", None) + + _request = build_datasets_get_request( + name=name, + version=version, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DatasetVersion, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @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 + DatasetVersion was deleted successfully or if the DatasetVersion does not exist. + + :param name: The name of the resource. Required. + :type name: str + :param version: The version of the DatasetVersion to delete. Required. + :type version: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_datasets_delete_request( + name=name, + version=version, + 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) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if cls: + return cls(pipeline_response, None, {}) # type: ignore + + @overload + def create_or_update( + self, + name: str, + version: str, + dataset_version: _models.DatasetVersion, + *, + content_type: str = "application/merge-patch+json", + **kwargs: Any + ) -> _models.DatasetVersion: + """Create a new or update an existing DatasetVersion with the given version id. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the DatasetVersion to create or update. Required. + :type version: str + :param dataset_version: The DatasetVersion to create or update. Required. + :type dataset_version: ~azure.ai.projects.models.DatasetVersion + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :return: DatasetVersion. The DatasetVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_or_update( + self, + name: str, + version: str, + dataset_version: JSON, + *, + content_type: str = "application/merge-patch+json", + **kwargs: Any + ) -> _models.DatasetVersion: + """Create a new or update an existing DatasetVersion with the given version id. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the DatasetVersion to create or update. Required. + :type version: str + :param dataset_version: The DatasetVersion to create or update. Required. + :type dataset_version: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :return: DatasetVersion. The DatasetVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_or_update( + self, + name: str, + version: str, + dataset_version: IO[bytes], + *, + content_type: str = "application/merge-patch+json", + **kwargs: Any + ) -> _models.DatasetVersion: + """Create a new or update an existing DatasetVersion with the given version id. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the DatasetVersion to create or update. Required. + :type version: str + :param dataset_version: The DatasetVersion to create or update. Required. + :type dataset_version: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :return: DatasetVersion. The DatasetVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + 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. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the DatasetVersion to create or update. Required. + :type version: str + :param dataset_version: The DatasetVersion to create or update. Is one of the following types: + DatasetVersion, JSON, IO[bytes] Required. + :type dataset_version: ~azure.ai.projects.models.DatasetVersion or JSON or IO[bytes] + :return: DatasetVersion. The DatasetVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.DatasetVersion] = kwargs.pop("cls", None) + + content_type = content_type or "application/merge-patch+json" + _content = None + if isinstance(dataset_version, (IOBase, bytes)): + _content = dataset_version + else: + _content = json.dumps(dataset_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_datasets_create_or_update_request( + name=name, + version=version, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 201]: + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DatasetVersion, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + def pending_upload( + self, + name: str, + version: str, + pending_upload_request: _models.PendingUploadRequest, + *, + 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. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the DatasetVersion to operate on. Required. + :type version: str + :param pending_upload_request: The pending upload request parameters. Required. + :type pending_upload_request: ~azure.ai.projects.models.PendingUploadRequest + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def pending_upload( + self, + name: str, + version: str, + pending_upload_request: JSON, + *, + 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. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the DatasetVersion to operate on. Required. + :type version: str + :param pending_upload_request: The pending upload request parameters. Required. + :type pending_upload_request: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def pending_upload( + self, + name: str, + version: str, + pending_upload_request: IO[bytes], + *, + 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. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the DatasetVersion to operate on. Required. + :type version: str + :param pending_upload_request: The pending upload request parameters. Required. + :type pending_upload_request: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def pending_upload( + self, + name: str, + version: str, + 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. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the DatasetVersion to operate on. Required. + :type version: str + :param pending_upload_request: The pending upload request parameters. Is one of the following + types: PendingUploadRequest, JSON, IO[bytes] Required. + :type pending_upload_request: ~azure.ai.projects.models.PendingUploadRequest or JSON or + IO[bytes] + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.PendingUploadResponse] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(pending_upload_request, (IOBase, bytes)): + _content = pending_upload_request + else: + _content = json.dumps(pending_upload_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_datasets_pending_upload_request( + name=name, + version=version, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.PendingUploadResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @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. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the DatasetVersion to operate on. Required. + :type version: str + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.DatasetCredential] = kwargs.pop("cls", None) + + _request = build_datasets_get_credentials_request( + name=name, + version=version, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DatasetCredential, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + +class DeploymentsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`deployments` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @distributed_trace + def get(self, name: str, **kwargs: Any) -> _models.Deployment: + """Get a deployed model. + + :param name: Name of the deployment. Required. + :type name: str + :return: Deployment. The Deployment is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Deployment + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.Deployment] = kwargs.pop("cls", None) + + _request = build_deployments_get_request( + name=name, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["x-ms-client-request-id"] = self._deserialize( + "str", response.headers.get("x-ms-client-request-id") + ) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Deployment, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list( + self, + *, + model_publisher: Optional[str] = None, + model_name: Optional[str] = None, + deployment_type: Optional[Union[str, _models.DeploymentType]] = None, + **kwargs: Any + ) -> ItemPaged["_models.Deployment"]: + """List all deployed models in the project. + + :keyword model_publisher: Model publisher to filter models by. Default value is None. + :paramtype model_publisher: str + :keyword model_name: Model name (the publisher specific name) to filter models by. Default + value is None. + :paramtype model_name: str + :keyword deployment_type: Type of deployment to filter list by. "ModelDeployment" Default value + is None. + :paramtype deployment_type: str or ~azure.ai.projects.models.DeploymentType + :return: An iterator like instance of Deployment + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Deployment] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.Deployment]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_deployments_list_request( + model_publisher=model_publisher, + model_name=model_name, + deployment_type=deployment_type, + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.Deployment], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + +class IndexesOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`indexes` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @distributed_trace + def list_versions(self, name: str, **kwargs: Any) -> ItemPaged["_models.Index"]: + """List all versions of the given Index. + + :param name: The name of the resource. Required. + :type name: str + :return: An iterator like instance of Index + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Index] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.Index]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_indexes_list_versions_request( + name=name, + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.Index], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def list(self, **kwargs: Any) -> ItemPaged["_models.Index"]: + """List the latest version of each Index. + + :return: An iterator like instance of Index + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Index] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.Index]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_indexes_list_request( + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.Index], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @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 + does not exist. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the Index to retrieve. Required. + :type version: str + :return: Index. The Index is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Index + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.Index] = kwargs.pop("cls", None) + + _request = build_indexes_get_request( + name=name, + version=version, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Index, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @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 + deleted successfully or if the Index does not exist. + + :param name: The name of the resource. Required. + :type name: str + :param version: The version of the Index to delete. Required. + :type version: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_indexes_delete_request( + name=name, + version=version, + 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) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if cls: + return cls(pipeline_response, None, {}) # type: ignore + + @overload + def create_or_update( + self, + name: str, + version: str, + index: _models.Index, + *, + content_type: str = "application/merge-patch+json", + **kwargs: Any + ) -> _models.Index: + """Create a new or update an existing Index with the given version id. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the Index to create or update. Required. + :type version: str + :param index: The Index to create or update. Required. + :type index: ~azure.ai.projects.models.Index + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :return: Index. The Index is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Index + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the Index to create or update. Required. + :type version: str + :param index: The Index to create or update. Required. + :type index: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :return: Index. The Index is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Index + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_or_update( + self, + name: str, + version: str, + index: IO[bytes], + *, + content_type: str = "application/merge-patch+json", + **kwargs: Any + ) -> _models.Index: + """Create a new or update an existing Index with the given version id. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the Index to create or update. Required. + :type version: str + :param index: The Index to create or update. Required. + :type index: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :return: Index. The Index is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Index + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + 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. + + :param name: The name of the resource. Required. + :type name: str + :param version: The specific version id of the Index to create or update. Required. + :type version: str + :param index: The Index to create or update. Is one of the following types: Index, JSON, + IO[bytes] Required. + :type index: ~azure.ai.projects.models.Index or JSON or IO[bytes] + :return: Index. The Index is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Index + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.Index] = kwargs.pop("cls", None) + + content_type = content_type or "application/merge-patch+json" + _content = None + if isinstance(index, (IOBase, bytes)): + _content = index + else: + _content = json.dumps(index, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_indexes_create_or_update_request( + name=name, + version=version, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 201]: + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Index, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + +class BetaAgentsOperations: # pylint: disable=too-many-public-methods + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`agents` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @overload + def patch_agent_details( + self, + agent_name: str, + *, + content_type: str = "application/merge-patch+json", + agent_endpoint: Optional[_models.AgentEndpointConfig] = None, + agent_card: Optional[_models.AgentCard] = None, + **kwargs: Any + ) -> _models.AgentDetails: + """Updates an agent endpoint. + + :param agent_name: The name of the agent to retrieve. Required. + :type agent_name: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :keyword agent_endpoint: The endpoint configuration for the agent. Default value is None. + :paramtype agent_endpoint: ~azure.ai.projects.models.AgentEndpointConfig + :keyword agent_card: Optional agent card for the agent. Default value is None. + :paramtype agent_card: ~azure.ai.projects.models.AgentCard + :return: AgentDetails. The AgentDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param agent_name: The name of the agent to retrieve. Required. + :type agent_name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :return: AgentDetails. The AgentDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param agent_name: The name of the agent to retrieve. Required. + :type agent_name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :return: AgentDetails. The AgentDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def patch_agent_details( + self, + agent_name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + agent_endpoint: Optional[_models.AgentEndpointConfig] = None, + agent_card: Optional[_models.AgentCard] = None, + **kwargs: Any + ) -> _models.AgentDetails: + """Updates an agent endpoint. + + :param agent_name: The name of the agent to retrieve. Required. + :type agent_name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword agent_endpoint: The endpoint configuration for the agent. Default value is None. + :paramtype agent_endpoint: ~azure.ai.projects.models.AgentEndpointConfig + :keyword agent_card: Optional agent card for the agent. Default value is None. + :paramtype agent_card: ~azure.ai.projects.models.AgentCard + :return: AgentDetails. The AgentDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.AgentDetails] = kwargs.pop("cls", None) + + if body is _Unset: + body = {"agent_card": agent_card, "agent_endpoint": agent_endpoint} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/merge-patch+json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_agents_patch_agent_details_request( + agent_name=agent_name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + def create_version_from_code( + self, + agent_name: str, + content: _models.CreateAgentVersionFromCodeContent, + *, + code_zip_sha256: str, + **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param content: Required. + :type content: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_version_from_code( + self, agent_name: str, content: JSON, *, code_zip_sha256: str, **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param content: Required. + :type content: JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def create_version_from_code( + self, + agent_name: str, + content: Union[_models.CreateAgentVersionFromCodeContent, JSON], + *, + code_zip_sha256: str, + **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param content: Is either a CreateAgentVersionFromCodeContent type or a JSON type. Required. + :type content: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent or JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.AgentVersionDetails] = kwargs.pop("cls", None) + + _body = content.as_dict() if isinstance(content, _Model) else content + _file_fields: list[str] = ["code"] + _data_fields: list[str] = ["metadata"] + _files = prepare_multipart_form_data(_body, _file_fields, _data_fields) + + _request = build_beta_agents_create_version_from_code_request( + agent_name=agent_name, + code_zip_sha256=code_zip_sha256, + api_version=self._config.api_version, + files=_files, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentVersionDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @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. + Returns the previously-uploaded zip (``application/zip``). + + If ``agent_version`` is supplied, returns that version's code zip; otherwise + returns the latest version's code zip. + + The SHA-256 digest of the returned bytes matches the ``content_hash`` on the + resolved version's ``code_configuration``. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :keyword agent_version: The version of the agent whose code zip should be downloaded. + If omitted, the latest version's code zip is returned. Default value is None. + :paramtype agent_version: str + :return: Iterator[bytes] + :rtype: Iterator[bytes] + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Iterator[bytes]] = kwargs.pop("cls", None) + + _request = build_beta_agents_download_code_request( + agent_name=agent_name, + agent_version=agent_version, + 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) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", True) + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["x-ms-agent-version"] = self._deserialize("str", response.headers.get("x-ms-agent-version")) + response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @overload + def create_session( + self, + agent_name: str, + *, + version_indicator: _models.VersionIndicator, + user_isolation_key: Optional[str] = None, + content_type: str = "application/json", + 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 + from ``version_indicator`` and enforces session ownership using the provided isolation key for + session-mutating operations. + + :param agent_name: The name of the agent to create a session for. Required. + :type agent_name: str + :keyword version_indicator: Determines which agent version backs the session. Required. + :paramtype version_indicator: ~azure.ai.projects.models.VersionIndicator + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword agent_session_id: Optional caller-provided session ID. If specified, it must be unique + within the agent endpoint. Auto-generated if omitted. Default value is None. + :paramtype agent_session_id: str + :return: AgentSessionResource. The AgentSessionResource is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentSessionResource + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_session( + self, + agent_name: str, + body: JSON, + *, + user_isolation_key: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.AgentSessionResource: + """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. + + :param agent_name: The name of the agent to create a session for. Required. + :type agent_name: str + :param body: Required. + :type body: JSON + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: AgentSessionResource. The AgentSessionResource is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentSessionResource + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_session( + self, + agent_name: str, + body: IO[bytes], + *, + user_isolation_key: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.AgentSessionResource: + """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. + + :param agent_name: The name of the agent to create a session for. Required. + :type agent_name: str + :param body: Required. + :type body: IO[bytes] + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: AgentSessionResource. The AgentSessionResource is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentSessionResource + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def create_session( + self, + agent_name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + version_indicator: _models.VersionIndicator = _Unset, + user_isolation_key: Optional[str] = None, + 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 + from ``version_indicator`` and enforces session ownership using the provided isolation key for + session-mutating operations. + + :param agent_name: The name of the agent to create a session for. Required. + :type agent_name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword version_indicator: Determines which agent version backs the session. Required. + :paramtype version_indicator: ~azure.ai.projects.models.VersionIndicator + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :keyword agent_session_id: Optional caller-provided session ID. If specified, it must be unique + within the agent endpoint. Auto-generated if omitted. Default value is None. + :paramtype agent_session_id: str + :return: AgentSessionResource. The AgentSessionResource is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentSessionResource + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.AgentSessionResource] = kwargs.pop("cls", None) + + if body is _Unset: + if version_indicator is _Unset: + raise TypeError("missing required argument: version_indicator") + body = {"agent_session_id": agent_session_id, "version_indicator": version_indicator} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_agents_create_session_request( + agent_name=agent_name, + user_isolation_key=user_isolation_key, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [201]: + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentSessionResource, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + 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. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param session_id: The session identifier. Required. + :type session_id: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :return: AgentSessionResource. The AgentSessionResource is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentSessionResource + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.AgentSessionResource] = kwargs.pop("cls", None) + + _request = build_beta_agents_get_session_request( + agent_name=agent_name, + session_id=session_id, + user_isolation_key=user_isolation_key, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentSessionResource, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + 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 + exist. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param session_id: The session identifier. Required. + :type session_id: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_agents_delete_session_request( + agent_name=agent_name, + session_id=session_id, + user_isolation_key=user_isolation_key, + 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) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + 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, None, {}) # type: ignore + + @distributed_trace + 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. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param session_id: The session identifier. Required. + :type session_id: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_agents_stop_session_request( + agent_name=agent_name, + session_id=session_id, + 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) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + 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, None, {}) # type: ignore + + @distributed_trace + def list_sessions( + self, + agent_name: str, + *, + user_isolation_key: Optional[str] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_models.AgentSessionResource"]: + """Returns a list of sessions for the specified agent. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of AgentSessionResource + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.AgentSessionResource] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.AgentSessionResource]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_beta_agents_list_sessions_request( + agent_name=agent_name, + user_isolation_key=user_isolation_key, + 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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.AgentSessionResource], + 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) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + 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 + as a Server-Sent Events (SSE) stream. + + Each SSE frame contains: + + * `event`: always `"log"` + * `data`: a plain-text log line (currently JSON-formatted, but the schema is not contractual and may include additional keys or change format over time; clients should treat it as an opaque string) + + Example SSE frames: + + .. code-block:: + + event: log + data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting FoundryCBAgent server on port 8088"} + + event: log + data: {"timestamp":"2026-03-10T09:33:17.130Z","stream":"stderr","message":"INFO: Application startup complete."} + + event: log + data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully connected to container"} + + event: log + data: {"timestamp":"2026-03-10T09:35:52.714Z","stream":"status","message":"No logs since last 60 seconds"} + + The stream remains open until the client disconnects or the server + terminates the connection. Clients should handle reconnection as needed. + + :param agent_name: The name of the hosted agent. Required. + :type agent_name: str + :param agent_version: The version of the agent. Required. + :type agent_version: str + :param session_id: The session ID (maps to an ADC sandbox). Required. + :type session_id: str + :return: SessionLogEvent. The SessionLogEvent is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SessionLogEvent + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.SessionLogEvent] = kwargs.pop("cls", None) + + _request = build_beta_agents_get_session_log_stream_request( + agent_name=agent_name, + agent_version=agent_version, + session_id=session_id, + 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) + + _decompress = kwargs.pop("decompress", True) + _stream = True + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.SessionLogEvent, response.text()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def _upload_session_file( + self, + agent_name: str, + agent_session_id: str, + content: bytes, + *, + path: str, + 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. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param agent_session_id: The session ID. Required. + :type agent_session_id: str + :param content: Required. + :type content: bytes + :keyword path: The destination file path within the sandbox, relative to the session home + directory. Required. + :paramtype path: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :return: SessionFileWriteResult. The SessionFileWriteResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SessionFileWriteResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: str = kwargs.pop("content_type", _headers.pop("Content-Type", "application/octet-stream")) + cls: ClsType[_models.SessionFileWriteResult] = kwargs.pop("cls", None) + + _content = content + + _request = build_beta_agents_upload_session_file_request( + agent_name=agent_name, + agent_session_id=agent_session_id, + path=path, + user_isolation_key=user_isolation_key, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [201]: + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.SessionFileWriteResult, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def download_session_file( + self, + agent_name: str, + agent_session_id: str, + *, + path: str, + user_isolation_key: Optional[str] = None, + **kwargs: Any + ) -> Iterator[bytes]: + """Download a file from the session sandbox as a binary stream. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param agent_session_id: The session ID. Required. + :type agent_session_id: str + :keyword path: The file path to download from the sandbox, relative to the session home + directory. Required. + :paramtype path: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :return: Iterator[bytes] + :rtype: Iterator[bytes] + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Iterator[bytes]] = kwargs.pop("cls", None) + + _request = build_beta_agents_download_session_file_request( + agent_name=agent_name, + agent_session_id=agent_session_id, + path=path, + user_isolation_key=user_isolation_key, + 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) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", True) + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_session_files( + self, + agent_name: str, + agent_session_id: str, + *, + path: Optional[str] = None, + user_isolation_key: Optional[str] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + 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. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param agent_session_id: The session ID. Required. + :type agent_session_id: str + :keyword path: The directory path to list, relative to the session home directory. Defaults to + the home directory if not provided. Default value is None. + :paramtype path: str + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of SessionDirectoryEntry + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.SessionDirectoryEntry] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.SessionDirectoryEntry]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_beta_agents_list_session_files_request( + agent_name=agent_name, + agent_session_id=agent_session_id, + path=path, + user_isolation_key=user_isolation_key, + 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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.SessionDirectoryEntry], + deserialized.get("entries", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, iter(list_of_elem) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def delete_session_file( # pylint: disable=inconsistent-return-statements + self, + agent_name: str, + agent_session_id: str, + *, + path: str, + recursive: Optional[bool] = None, + 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. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param agent_session_id: The session ID. Required. + :type agent_session_id: str + :keyword path: The file or directory path to delete, relative to the session home directory. + Required. + :paramtype path: str + :keyword recursive: Whether to recursively delete directory contents. Defaults to false. + Default value is None. + :paramtype recursive: bool + :keyword user_isolation_key: Opaque per-user isolation key used to scope endpoint-scoped data + (responses, conversations, sessions) to a specific end user. Default value is None. + :paramtype user_isolation_key: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_agents_delete_session_file_request( + agent_name=agent_name, + agent_session_id=agent_session_id, + path=path, + recursive=recursive, + user_isolation_key=user_isolation_key, + 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) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + 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, None, {}) # type: ignore + + @overload + def create_optimization_job( + self, + inputs: _models.OptimizationJobInputs, + *, + 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: ~azure.ai.projects.models.OptimizationJobInputs + :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 + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_optimization_job( + self, inputs: 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 + :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 + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_optimization_job( + self, + inputs: IO[bytes], + *, + 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: 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 + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def create_optimization_job( + self, + inputs: Union[_models.OptimizationJobInputs, 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] + :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 + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.OptimizationJob] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(inputs, (IOBase, bytes)): + _content = inputs + else: + _content = json.dumps(inputs, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_agents_create_optimization_job_request( + operation_id=operation_id, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [201]: + 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, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) + response_headers["Location"] = self._deserialize("str", response.headers.get("Location")) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.OptimizationJob, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + 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. + + :param job_id: The ID of the job. Required. + :type job_id: str + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.OptimizationJob] = kwargs.pop("cls", None) + + _request = build_beta_agents_get_optimization_job_request( + job_id=job_id, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 202]: + 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, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Retry-After"] = self._deserialize("int", response.headers.get("Retry-After")) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.OptimizationJob, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_optimization_jobs( + self, + *, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + status: Optional[Union[str, _models.JobStatus]] = None, + agent_name: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_models.OptimizationJob"]: + """Returns a list of agent optimization jobs. + + List optimization jobs. Supports cursor pagination and optional status / agent_name filters. + + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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 + :keyword status: Filter to jobs in this lifecycle state. Known values are: "queued", + "in_progress", "succeeded", "failed", and "cancelled". Default value is None. + :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] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.OptimizationJob]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_beta_agents_list_optimization_jobs_request( + limit=limit, + order=order, + after=_continuation_token, + before=before, + status=status, + agent_name=agent_name, + 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 - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.AgentDetails], - 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) + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.OptimizationJob], + 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) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def cancel_optimization_job(self, job_id: str, **kwargs: Any) -> _models.OptimizationJob: + """Cancels an agent optimization job. + + Request cancellation. Idempotent on terminal states. + + :param job_id: The ID of the job to cancel. Required. + :type job_id: str + :return: OptimizationJob. The OptimizationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.OptimizationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.OptimizationJob] = kwargs.pop("cls", None) + + _request = build_beta_agents_cancel_optimization_job_request( + job_id=job_id, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.OptimizationJob, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def delete_optimization_job( # pylint: disable=inconsistent-return-statements + self, job_id: str, *, force: Optional[bool] = None, **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: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_agents_delete_optimization_job_request( + job_id=job_id, + force=force, + 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) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + 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, None, {}) # type: ignore + + @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: + """Returns a list of candidates for an optimization job. + + List candidates produced by a job. + + :param job_id: The optimization job id. Required. + :type job_id: str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + 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) + + _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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentsPagedResultOptimizationCandidate, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def get_optimization_candidate(self, job_id: str, candidate_id: str, **kwargs: Any) -> _models.CandidateMetadata: + """Get a candidate by id. + + Get a single candidate's metadata, manifest, and promotion info. + + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id. Required. + :type candidate_id: str + :return: CandidateMetadata. The CandidateMetadata is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CandidateMetadata + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) - def get_next(_continuation_token=None): - _request = prepare_request(_continuation_token) + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response + cls: ClsType[_models.CandidateMetadata] = kwargs.pop("cls", None) - 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) + _request = build_beta_agents_get_optimization_candidate_request( + job_id=job_id, + candidate_id=candidate_id, + 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 pipeline_response + _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 + ) - return ItemPaged(get_next, extract_data) + response = pipeline_response.http_response - @overload - def create_version( - self, - agent_name: str, - *, - definition: _models.AgentDefinition, - content_type: str = "application/json", - metadata: Optional[dict[str, str]] = None, - description: Optional[str] = None, - blueprint_reference: Optional[_models.AgentBlueprintReference] = None, - **kwargs: Any - ) -> _models.AgentVersionDetails: - """Create a new agent version. + 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, + ) + raise HttpResponseError(response=response, model=error) - :param agent_name: The unique name that identifies the agent. Name can be used to - retrieve/update/delete the agent. + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.CandidateMetadata, response.json()) - * Must start and end with alphanumeric characters, - * Can contain hyphens in the middle - * Must not exceed 63 characters. Required. - :type agent_name: str - :keyword definition: The agent definition. This can be a workflow, hosted agent, or a simple - agent definition. Required. - :paramtype definition: ~azure.ai.projects.models.AgentDefinition - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :keyword description: A human-readable description of the agent. Default value is None. - :paramtype description: str - :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. - :paramtype blueprint_reference: ~azure.ai.projects.models.AgentBlueprintReference - :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentVersionDetails + return deserialized # type: ignore + + @distributed_trace + def get_optimization_candidate_config( + self, job_id: str, candidate_id: str, **kwargs: Any + ) -> _models.CandidateDeployConfig: + """Get candidate deploy config. + + Get the candidate's deploy config JSON. Used to compose ``agents.create_version(...)`` from a + candidate. + + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id. Required. + :type candidate_id: str + :return: CandidateDeployConfig. The CandidateDeployConfig is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CandidateDeployConfig :raises ~azure.core.exceptions.HttpResponseError: """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) - @overload - def create_version( - self, agent_name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.AgentVersionDetails: - """Create a new agent version. + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - :param agent_name: The unique name that identifies the agent. Name can be used to - retrieve/update/delete the agent. + cls: ClsType[_models.CandidateDeployConfig] = kwargs.pop("cls", None) - * Must start and end with alphanumeric characters, - * Can contain hyphens in the middle - * Must not exceed 63 characters. Required. - :type agent_name: str - :param body: Required. - :type body: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentVersionDetails - :raises ~azure.core.exceptions.HttpResponseError: - """ + _request = build_beta_agents_get_optimization_candidate_config_request( + job_id=job_id, + candidate_id=candidate_id, + 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) - @overload - def create_version( - self, agent_name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.AgentVersionDetails: - """Create a new agent version. + _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 + ) - :param agent_name: The unique name that identifies the agent. Name can be used to - retrieve/update/delete the agent. + response = pipeline_response.http_response - * Must start and end with alphanumeric characters, - * Can contain hyphens in the middle - * Must not exceed 63 characters. Required. - :type agent_name: str - :param body: Required. - :type body: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentVersionDetails + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.CandidateDeployConfig, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def get_optimization_candidate_results( + self, job_id: str, candidate_id: str, **kwargs: Any + ) -> _models.CandidateResults: + """Get candidate evaluation results. + + Get full per-task evaluation results for a candidate. + + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id. Required. + :type candidate_id: str + :return: CandidateResults. The CandidateResults is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CandidateResults :raises ~azure.core.exceptions.HttpResponseError: """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) - @distributed_trace - def create_version( - self, - agent_name: str, - body: Union[JSON, IO[bytes]] = _Unset, - *, - definition: _models.AgentDefinition = _Unset, - metadata: Optional[dict[str, str]] = None, - description: Optional[str] = None, - blueprint_reference: Optional[_models.AgentBlueprintReference] = None, - **kwargs: Any - ) -> _models.AgentVersionDetails: - """Create a new agent version. + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - :param agent_name: The unique name that identifies the agent. Name can be used to - retrieve/update/delete the agent. + cls: ClsType[_models.CandidateResults] = kwargs.pop("cls", None) + + _request = build_beta_agents_get_optimization_candidate_results_request( + job_id=job_id, + candidate_id=candidate_id, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.CandidateResults, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def get_candidate_file(self, job_id: str, candidate_id: str, *, path: str, **kwargs: Any) -> Iterator[bytes]: + """Get a candidate file. - * Must start and end with alphanumeric characters, - * Can contain hyphens in the middle - * Must not exceed 63 characters. Required. - :type agent_name: str - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword definition: The agent definition. This can be a workflow, hosted agent, or a simple - agent definition. Required. - :paramtype definition: ~azure.ai.projects.models.AgentDefinition - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. + Stream a specific file from the candidate's blob directory. - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :keyword description: A human-readable description of the agent. Default value is None. - :paramtype description: str - :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. - :paramtype blueprint_reference: ~azure.ai.projects.models.AgentBlueprintReference - :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentVersionDetails + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id. Required. + :type candidate_id: str + :keyword path: Relative path of the file to download (e.g. 'files/examples.jsonl'). Required. + :paramtype path: str + :return: Iterator[bytes] + :rtype: Iterator[bytes] :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -2715,34 +8809,16 @@ def create_version( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.AgentVersionDetails] = kwargs.pop("cls", None) - - if body is _Unset: - if definition is _Unset: - raise TypeError("missing required argument: definition") - body = { - "blueprint_reference": blueprint_reference, - "definition": definition, - "description": description, - "metadata": metadata, - } - body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[Iterator[bytes]] = kwargs.pop("cls", None) - _request = build_agents_create_version_request( - agent_name=agent_name, - content_type=content_type, + _request = build_beta_agents_get_candidate_file_request( + job_id=job_id, + candidate_id=candidate_id, + path=path, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -2752,7 +8828,7 @@ def create_version( _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = kwargs.pop("stream", True) pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) @@ -2772,10 +8848,7 @@ def create_version( ) raise HttpResponseError(response=response, model=error) - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.AgentVersionDetails, response.json()) + deserialized = response.iter_bytes() if _decompress else response.iter_raw() if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -2783,133 +8856,114 @@ def create_version( return deserialized # type: ignore @overload - def create_version_from_manifest( + def promote_candidate( self, - agent_name: str, + job_id: str, + candidate_id: str, + candidate_request: _models.PromoteCandidateRequest, *, - manifest_id: str, - parameter_values: dict[str, Any], content_type: str = "application/json", - metadata: Optional[dict[str, str]] = None, - description: Optional[str] = None, **kwargs: Any - ) -> _models.AgentVersionDetails: - """Create a new agent version from a manifest. + ) -> _models.PromoteCandidateResponse: + """Promote a candidate. - :param agent_name: The unique name that identifies the agent. Name can be used to - retrieve/update/delete the agent. + Promotes a candidate, recording the deployment timestamp and target agent version. - * Must start and end with alphanumeric characters, - * Can contain hyphens in the middle - * Must not exceed 63 characters. Required. - :type agent_name: str - :keyword manifest_id: The manifest ID to import the agent version from. Required. - :paramtype manifest_id: str - :keyword parameter_values: The inputs to the manifest that will result in a fully materialized - Agent. Required. - :paramtype parameter_values: dict[str, any] + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id to promote. Required. + :type candidate_id: str + :param candidate_request: Promotion details. Required. + :type candidate_request: ~azure.ai.projects.models.PromoteCandidateRequest :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :keyword description: A human-readable description of the agent. Default value is None. - :paramtype description: str - :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentVersionDetails + :return: PromoteCandidateResponse. The PromoteCandidateResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.PromoteCandidateResponse :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + def promote_candidate( + self, + job_id: str, + candidate_id: str, + candidate_request: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.PromoteCandidateResponse: + """Promote a candidate. - :param agent_name: The unique name that identifies the agent. Name can be used to - retrieve/update/delete the agent. + Promotes a candidate, recording the deployment timestamp and target agent version. - * Must start and end with alphanumeric characters, - * Can contain hyphens in the middle - * Must not exceed 63 characters. Required. - :type agent_name: str - :param body: Required. - :type body: JSON + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id to promote. Required. + :type candidate_id: str + :param candidate_request: Promotion details. Required. + :type candidate_request: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentVersionDetails + :return: PromoteCandidateResponse. The PromoteCandidateResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.PromoteCandidateResponse :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + def promote_candidate( + self, + job_id: str, + candidate_id: str, + candidate_request: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.PromoteCandidateResponse: + """Promote a candidate. - :param agent_name: The unique name that identifies the agent. Name can be used to - retrieve/update/delete the agent. + Promotes a candidate, recording the deployment timestamp and target agent version. - * Must start and end with alphanumeric characters, - * Can contain hyphens in the middle - * Must not exceed 63 characters. Required. - :type agent_name: str - :param body: Required. - :type body: IO[bytes] + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id to promote. Required. + :type candidate_id: str + :param candidate_request: Promotion details. Required. + :type candidate_request: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentVersionDetails + :return: PromoteCandidateResponse. The PromoteCandidateResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.PromoteCandidateResponse :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def create_version_from_manifest( + def promote_candidate( self, - agent_name: str, - body: Union[JSON, IO[bytes]] = _Unset, - *, - manifest_id: str = _Unset, - parameter_values: dict[str, Any] = _Unset, - metadata: Optional[dict[str, str]] = None, - description: Optional[str] = None, + job_id: str, + candidate_id: str, + candidate_request: Union[_models.PromoteCandidateRequest, JSON, IO[bytes]], **kwargs: Any - ) -> _models.AgentVersionDetails: - """Create a new agent version from a manifest. - - :param agent_name: The unique name that identifies the agent. Name can be used to - retrieve/update/delete the agent. - - * Must start and end with alphanumeric characters, - * Can contain hyphens in the middle - * Must not exceed 63 characters. Required. - :type agent_name: str - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword manifest_id: The manifest ID to import the agent version from. Required. - :paramtype manifest_id: str - :keyword parameter_values: The inputs to the manifest that will result in a fully materialized - Agent. Required. - :paramtype parameter_values: dict[str, any] - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :keyword description: A human-readable description of the agent. Default value is None. - :paramtype description: str - :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentVersionDetails + ) -> _models.PromoteCandidateResponse: + """Promote a candidate. + + Promotes a candidate, recording the deployment timestamp and target agent version. + + :param job_id: The optimization job id. Required. + :type job_id: str + :param candidate_id: The candidate id to promote. Required. + :type candidate_id: str + :param candidate_request: Promotion details. Is one of the following types: + PromoteCandidateRequest, JSON, IO[bytes] Required. + :type candidate_request: ~azure.ai.projects.models.PromoteCandidateRequest or JSON or IO[bytes] + :return: PromoteCandidateResponse. The PromoteCandidateResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.PromoteCandidateResponse :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -2924,29 +8978,18 @@ def create_version_from_manifest( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.AgentVersionDetails] = kwargs.pop("cls", None) + cls: ClsType[_models.PromoteCandidateResponse] = kwargs.pop("cls", None) - if body is _Unset: - if manifest_id is _Unset: - raise TypeError("missing required argument: manifest_id") - if parameter_values is _Unset: - raise TypeError("missing required argument: parameter_values") - body = { - "description": description, - "manifest_id": manifest_id, - "metadata": metadata, - "parameter_values": parameter_values, - } - body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None - if isinstance(body, (IOBase, bytes)): - _content = body + if isinstance(candidate_request, (IOBase, bytes)): + _content = candidate_request else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(candidate_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_agents_create_version_from_manifest_request( - agent_name=agent_name, + _request = build_beta_agents_promote_candidate_request( + job_id=job_id, + candidate_id=candidate_id, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -2982,92 +9025,39 @@ def create_version_from_manifest( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.AgentVersionDetails, response.json()) + deserialized = _deserialize(_models.PromoteCandidateResponse, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @distributed_trace - def get_version(self, agent_name: str, agent_version: str, **kwargs: Any) -> _models.AgentVersionDetails: - """Retrieves a specific version of an agent. - - :param agent_name: The name of the agent to retrieve. Required. - :type agent_name: str - :param agent_version: The version of the agent to retrieve. Required. - :type agent_version: str - :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentVersionDetails - :raises ~azure.core.exceptions.HttpResponseError: - """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[_models.AgentVersionDetails] = kwargs.pop("cls", None) - - _request = build_agents_get_version_request( - agent_name=agent_name, - agent_version=agent_version, - 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) - - _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 - ) - - response = pipeline_response.http_response - - 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, - ) - raise HttpResponseError(response=response, model=error) - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.AgentVersionDetails, response.json()) +class BetaEvaluationTaxonomiesOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`evaluation_taxonomies` attribute. + """ - return deserialized # type: ignore + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") @distributed_trace - def delete_version(self, agent_name: str, agent_version: str, **kwargs: Any) -> _models.DeleteAgentVersionResponse: - """Deletes a specific version of an agent. + def get(self, name: str, **kwargs: Any) -> _models.EvaluationTaxonomy: + """Get an evaluation run by name. - :param agent_name: The name of the agent to delete. Required. - :type agent_name: str - :param agent_version: The version of the agent to delete. Required. - :type agent_version: str - :return: DeleteAgentVersionResponse. The DeleteAgentVersionResponse is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.DeleteAgentVersionResponse + :param name: The name of the resource. Required. + :type name: str + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -3081,11 +9071,10 @@ def delete_version(self, agent_name: str, agent_version: str, **kwargs: Any) -> _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.DeleteAgentVersionResponse] = kwargs.pop("cls", None) + cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) - _request = build_agents_delete_version_request( - agent_name=agent_name, - agent_version=agent_version, + _request = build_beta_evaluation_taxonomies_get_request( + name=name, api_version=self._config.api_version, headers=_headers, params=_params, @@ -3110,16 +9099,12 @@ def delete_version(self, agent_name: str, agent_version: str, **kwargs: Any) -> except (StreamConsumedError, StreamClosedError): pass 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) + raise HttpResponseError(response=response) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.DeleteAgentVersionResponse, response.json()) + deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -3127,41 +9112,23 @@ def delete_version(self, agent_name: str, agent_version: str, **kwargs: Any) -> return deserialized # type: ignore @distributed_trace - def list_versions( - self, - agent_name: str, - *, - limit: Optional[int] = None, - order: Optional[Union[str, _models.PageOrder]] = None, - before: Optional[str] = None, - **kwargs: Any - ) -> ItemPaged["_models.AgentVersionDetails"]: - """Returns the list of versions of an agent. + def list( + self, *, input_name: Optional[str] = None, input_type: Optional[str] = None, **kwargs: Any + ) -> ItemPaged["_models.EvaluationTaxonomy"]: + """List evaluation taxonomies. - :param agent_name: The name of the agent to retrieve versions for. Required. - :type agent_name: str - :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the - default is 20. Default value is None. - :paramtype limit: int - :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for - 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 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: An iterator like instance of AgentVersionDetails - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.AgentVersionDetails] + :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. + :paramtype input_type: str + :return: An iterator like instance of EvaluationTaxonomy + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.EvaluationTaxonomy] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.AgentVersionDetails]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.EvaluationTaxonomy]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -3171,36 +9138,60 @@ def list_versions( } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(_continuation_token=None): + def prepare_request(next_link=None): + if not next_link: + + _request = build_beta_evaluation_taxonomies_list_request( + input_name=input_name, + input_type=input_type, + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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_agents_list_versions_request( - agent_name=agent_name, - 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 def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.AgentVersionDetails], - deserialized.get("data", []), + List[_models.EvaluationTaxonomy], + deserialized.get("value", []), ) if cls: list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("last_id") or None, iter(list_of_elem) + return deserialized.get("nextLink") or None, iter(list_of_elem) - def get_next(_continuation_token=None): - _request = prepare_request(_continuation_token) + def get_next(next_link=None): + _request = prepare_request(next_link) _stream = False pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access @@ -3210,42 +9201,20 @@ def get_next(_continuation_token=None): 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) + raise HttpResponseError(response=response) return pipeline_response return ItemPaged(get_next, extract_data) - -class EvaluationRulesOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`evaluation_rules` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - @distributed_trace - def get(self, id: str, **kwargs: Any) -> _models.EvaluationRule: - """Get an evaluation rule. + def delete(self, name: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements + """Delete an evaluation taxonomy by name. - :param id: Unique identifier for the evaluation rule. Required. - :type id: str - :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationRule + :param name: The name of the resource. Required. + :type name: str + :return: None + :rtype: None :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -3259,10 +9228,10 @@ def get(self, id: str, **kwargs: Any) -> _models.EvaluationRule: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.EvaluationRule] = kwargs.pop("cls", None) + cls: ClsType[None] = kwargs.pop("cls", None) - _request = build_evaluation_rules_get_request( - id=id, + _request = build_beta_evaluation_taxonomies_delete_request( + name=name, api_version=self._config.api_version, headers=_headers, params=_params, @@ -3272,41 +9241,87 @@ def get(self, id: str, **kwargs: Any) -> _models.EvaluationRule: } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = False pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - 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 + if response.status_code not in [204]: map_error(status_code=response.status_code, response=response, error_map=error_map) raise HttpResponseError(response=response) - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.EvaluationRule, response.json()) - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, None, {}) # type: ignore - return deserialized # type: ignore + @overload + def create( + self, name: str, taxonomy: _models.EvaluationTaxonomy, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Create an evaluation taxonomy. + + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: ~azure.ai.projects.models.EvaluationTaxonomy + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create( + self, name: str, taxonomy: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Create an evaluation taxonomy. + + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create( + self, name: str, taxonomy: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Create an evaluation taxonomy. + + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :raises ~azure.core.exceptions.HttpResponseError: + """ @distributed_trace - def delete(self, id: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements - """Delete an evaluation rule. + def create( + self, name: str, taxonomy: Union[_models.EvaluationTaxonomy, JSON, IO[bytes]], **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Create an evaluation taxonomy. - :param id: Unique identifier for the evaluation rule. Required. - :type id: str - :return: None - :rtype: None + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Is one of the following types: EvaluationTaxonomy, + JSON, IO[bytes] Required. + :type taxonomy: ~azure.ai.projects.models.EvaluationTaxonomy or JSON or IO[bytes] + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -3317,14 +9332,24 @@ def delete(self, id: str, **kwargs: Any) -> None: # pylint: disable=inconsisten } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[None] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) - _request = build_evaluation_rules_delete_request( - id=id, + content_type = content_type or "application/json" + _content = None + if isinstance(taxonomy, (IOBase, bytes)): + _content = taxonomy + else: + _content = json.dumps(taxonomy, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_evaluation_taxonomies_create_request( + name=name, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -3333,87 +9358,100 @@ def delete(self, id: str, **kwargs: Any) -> None: # pylint: disable=inconsisten } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _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 ) response = pipeline_response.http_response - if response.status_code not in [204]: + if response.status_code not in [200, 201]: + 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) raise HttpResponseError(response=response) + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) + if cls: - return cls(pipeline_response, None, {}) # type: ignore + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore @overload - def create_or_update( - self, id: str, evaluation_rule: _models.EvaluationRule, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationRule: - """Create or update an evaluation rule. + def update( + self, name: str, taxonomy: _models.EvaluationTaxonomy, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Update an evaluation taxonomy. - :param id: Unique identifier for the evaluation rule. Required. - :type id: str - :param evaluation_rule: Evaluation rule resource. Required. - :type evaluation_rule: ~azure.ai.projects.models.EvaluationRule + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: ~azure.ai.projects.models.EvaluationTaxonomy :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationRule + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def create_or_update( - self, id: str, evaluation_rule: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationRule: - """Create or update an evaluation rule. + def update( + self, name: str, taxonomy: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Update an evaluation taxonomy. - :param id: Unique identifier for the evaluation rule. Required. - :type id: str - :param evaluation_rule: Evaluation rule resource. Required. - :type evaluation_rule: JSON + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationRule + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def create_or_update( - self, id: str, evaluation_rule: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationRule: - """Create or update an evaluation rule. + def update( + self, name: str, taxonomy: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Update an evaluation taxonomy. - :param id: Unique identifier for the evaluation rule. Required. - :type id: str - :param evaluation_rule: Evaluation rule resource. Required. - :type evaluation_rule: IO[bytes] + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Required. + :type taxonomy: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationRule + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def create_or_update( - self, id: str, evaluation_rule: Union[_models.EvaluationRule, JSON, IO[bytes]], **kwargs: Any - ) -> _models.EvaluationRule: - """Create or update an evaluation rule. + def update( + self, name: str, taxonomy: Union[_models.EvaluationTaxonomy, JSON, IO[bytes]], **kwargs: Any + ) -> _models.EvaluationTaxonomy: + """Update an evaluation taxonomy. - :param id: Unique identifier for the evaluation rule. Required. - :type id: str - :param evaluation_rule: Evaluation rule resource. Is one of the following types: - EvaluationRule, JSON, IO[bytes] Required. - :type evaluation_rule: ~azure.ai.projects.models.EvaluationRule or JSON or IO[bytes] - :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationRule + :param name: The name of the evaluation taxonomy. Required. + :type name: str + :param taxonomy: The evaluation taxonomy. Is one of the following types: EvaluationTaxonomy, + JSON, IO[bytes] Required. + :type taxonomy: ~azure.ai.projects.models.EvaluationTaxonomy or JSON or IO[bytes] + :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluationTaxonomy :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -3428,17 +9466,17 @@ def create_or_update( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.EvaluationRule] = kwargs.pop("cls", None) + cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) content_type = content_type or "application/json" _content = None - if isinstance(evaluation_rule, (IOBase, bytes)): - _content = evaluation_rule + if isinstance(taxonomy, (IOBase, bytes)): + _content = taxonomy else: - _content = json.dumps(evaluation_rule, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(taxonomy, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_evaluation_rules_create_or_update_request( - id=id, + _request = build_beta_evaluation_taxonomies_update_request( + name=name, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -3458,7 +9496,7 @@ def create_or_update( response = pipeline_response.http_response - if response.status_code not in [200, 201]: + if response.status_code not in [200]: if _stream: try: response.read() # Load the body in memory and close the socket @@ -3470,39 +9508,163 @@ def create_or_update( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluationRule, response.json()) + deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + +class BetaEvaluatorsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`evaluators` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + @distributed_trace - def list( + def list_versions( self, + name: str, *, - action_type: Optional[Union[str, _models.EvaluationRuleActionType]] = None, - agent_name: Optional[str] = None, - enabled: Optional[bool] = None, + type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, + limit: Optional[int] = None, **kwargs: Any - ) -> ItemPaged["_models.EvaluationRule"]: - """List all evaluation rules. + ) -> ItemPaged["_models.EvaluatorVersion"]: + """List all versions of the given evaluator. - :keyword action_type: Filter by the type of evaluation rule. Known values are: - "continuousEvaluation" and "humanEvaluationPreview". Default value is None. - :paramtype action_type: str or ~azure.ai.projects.models.EvaluationRuleActionType - :keyword agent_name: Filter by the agent name. Default value is None. - :paramtype agent_name: str - :keyword enabled: Filter by the enabled status. Default value is None. - :paramtype enabled: bool - :return: An iterator like instance of EvaluationRule - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.EvaluationRule] + :param name: The name of the resource. Required. + :type name: str + :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 + value is None. + :paramtype type: str or str or str or str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. Default value is None. + :paramtype limit: int + :return: An iterator like instance of EvaluatorVersion + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.EvaluatorVersion] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.EvaluatorVersion]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_beta_evaluators_list_versions_request( + name=name, + type=type, + limit=limit, + 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) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.EvaluatorVersion], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def list( + self, + *, + type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, + limit: Optional[int] = None, + **kwargs: Any + ) -> ItemPaged["_models.EvaluatorVersion"]: + """List 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 + value is None. + :paramtype type: str or str or str or str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. Default value is None. + :paramtype limit: int + :return: An iterator like instance of EvaluatorVersion + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.EvaluatorVersion] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.EvaluationRule]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.EvaluatorVersion]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -3515,10 +9677,9 @@ def list( def prepare_request(next_link=None): if not next_link: - _request = build_evaluation_rules_list_request( - action_type=action_type, - agent_name=agent_name, - enabled=enabled, + _request = build_beta_evaluators_list_request( + type=type, + limit=limit, api_version=self._config.api_version, headers=_headers, params=_params, @@ -3541,7 +9702,10 @@ def prepare_request(next_link=None): ) _next_request_params["api-version"] = self._config.api_version _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, ) path_format_arguments = { "endpoint": self._serialize.url( @@ -3555,7 +9719,7 @@ def prepare_request(next_link=None): def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.EvaluationRule], + List[_models.EvaluatorVersion], deserialized.get("value", []), ) if cls: @@ -3579,32 +9743,17 @@ def get_next(next_link=None): return ItemPaged(get_next, extract_data) - -class ConnectionsOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`connections` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - @distributed_trace - def _get(self, name: str, **kwargs: Any) -> _models.Connection: - """Get a connection by name, without populating connection credentials. + 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. - :param name: The friendly name of the connection, provided by the user. Required. + :param name: The name of the resource. Required. :type name: str - :return: Connection. The Connection is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Connection + :param version: The specific version id of the EvaluatorVersion to retrieve. Required. + :type version: str + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -3618,10 +9767,11 @@ def _get(self, name: str, **kwargs: Any) -> _models.Connection: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.Connection] = kwargs.pop("cls", None) + cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) - _request = build_connections_get_request( + _request = build_beta_evaluators_get_version_request( name=name, + version=version, api_version=self._config.api_version, headers=_headers, params=_params, @@ -3648,29 +9798,29 @@ def _get(self, name: str, **kwargs: Any) -> _models.Connection: map_error(status_code=response.status_code, response=response, error_map=error_map) raise HttpResponseError(response=response) - response_headers = {} - response_headers["x-ms-client-request-id"] = self._deserialize( - "str", response.headers.get("x-ms-client-request-id") - ) - if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.Connection, response.json()) + deserialized = _deserialize(_models.EvaluatorVersion, response.json()) if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore + return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore @distributed_trace - def _get_with_credentials(self, name: str, **kwargs: Any) -> _models.Connection: - """Get a connection by name, with its connection credentials. + 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. - :param name: The friendly name of the connection, provided by the user. Required. + :param name: The name of the resource. Required. :type name: str - :return: Connection. The Connection is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Connection + :param version: The version of the EvaluatorVersion to delete. Required. + :type version: str + :return: None + :rtype: None :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -3684,10 +9834,11 @@ def _get_with_credentials(self, name: str, **kwargs: Any) -> _models.Connection: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.Connection] = kwargs.pop("cls", None) + cls: ClsType[None] = kwargs.pop("cls", None) - _request = build_connections_get_with_credentials_request( + _request = build_beta_evaluators_delete_version_request( name=name, + version=version, api_version=self._config.api_version, headers=_headers, params=_params, @@ -3697,171 +9848,94 @@ def _get_with_credentials(self, name: str, **kwargs: Any) -> _models.Connection: } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = False pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - 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 + if response.status_code not in [204]: map_error(status_code=response.status_code, response=response, error_map=error_map) raise HttpResponseError(response=response) - response_headers = {} - response_headers["x-ms-client-request-id"] = self._deserialize( - "str", response.headers.get("x-ms-client-request-id") - ) - - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.Connection, response.json()) - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore + return cls(pipeline_response, None, {}) # type: ignore - @distributed_trace - def list( + @overload + def create_version( self, + name: str, + evaluator_version: _models.EvaluatorVersion, *, - connection_type: Optional[Union[str, _models.ConnectionType]] = None, - default_connection: Optional[bool] = None, + content_type: str = "application/json", **kwargs: Any - ) -> ItemPaged["_models.Connection"]: - """List all connections in the project, without populating connection credentials. + ) -> _models.EvaluatorVersion: + """Create a new EvaluatorVersion with auto incremented version id. - :keyword connection_type: List 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 - None. - :paramtype default_connection: bool - :return: An iterator like instance of Connection - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Connection] + :param name: The name of the resource. Required. + :type name: str + :param evaluator_version: Required. + :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[List[_models.Connection]] = kwargs.pop("cls", None) - - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_connections_list_request( - connection_type=connection_type, - default_connection=default_connection, - 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) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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 - - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.Connection], - deserialized.get("value", []), - ) - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) - - def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - return pipeline_response - - return ItemPaged(get_next, extract_data) - - -class DatasetsOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`datasets` attribute. - """ + @overload + 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. - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + :param name: The name of the resource. Required. + :type name: str + :param evaluator_version: Required. + :type evaluator_version: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ - @distributed_trace - def list_versions(self, name: str, **kwargs: Any) -> ItemPaged["_models.DatasetVersion"]: - """List all versions of the given DatasetVersion. + @overload + 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. :param name: The name of the resource. Required. :type name: str - :return: An iterator like instance of DatasetVersion - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.DatasetVersion] + :param evaluator_version: Required. + :type evaluator_version: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.DatasetVersion]] = kwargs.pop("cls", None) + @distributed_trace + 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. + :param name: The name of the resource. Required. + :type name: str + :param evaluator_version: Is one of the following types: EvaluatorVersion, JSON, IO[bytes] + Required. + :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion or JSON or IO[bytes] + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -3870,167 +9944,150 @@ def list_versions(self, name: str, **kwargs: Any) -> ItemPaged["_models.DatasetV } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(next_link=None): - if not next_link: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} - _request = build_datasets_list_versions_request( - name=name, - 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) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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) + content_type = content_type or "application/json" + _content = None + if isinstance(evaluator_version, (IOBase, bytes)): + _content = evaluator_version + else: + _content = json.dumps(evaluator_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - return _request + _request = build_beta_evaluators_create_version_request( + name=name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.DatasetVersion], - deserialized.get("value", []), - ) - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) + _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 get_next(next_link=None): - _request = prepare_request(next_link) + response = pipeline_response.http_response - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response + if response.status_code not in [201]: + 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) + raise HttpResponseError(response=response) - if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.EvaluatorVersion, response.json()) - return pipeline_response + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore - return ItemPaged(get_next, extract_data) + return deserialized # type: ignore - @distributed_trace - def list(self, **kwargs: Any) -> ItemPaged["_models.DatasetVersion"]: - """List the latest version of each DatasetVersion. + @overload + def update_version( + self, + name: str, + version: str, + evaluator_version: _models.EvaluatorVersion, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.EvaluatorVersion: + """Update an existing EvaluatorVersion with the given version id. - :return: An iterator like instance of DatasetVersion - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.DatasetVersion] + :param name: The name of the resource. Required. + :type name: str + :param version: The version of the EvaluatorVersion to update. Required. + :type version: str + :param evaluator_version: Evaluator resource. Required. + :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[List[_models.DatasetVersion]] = kwargs.pop("cls", None) - - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_datasets_list_request( - 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) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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 - - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.DatasetVersion], - deserialized.get("value", []), - ) - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) - - def get_next(next_link=None): - _request = prepare_request(next_link) - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response + @overload + 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. - if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + :param name: The name of the resource. Required. + :type name: str + :param version: The version of the EvaluatorVersion to update. Required. + :type version: str + :param evaluator_version: Evaluator resource. Required. + :type evaluator_version: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ - return pipeline_response + @overload + def update_version( + self, + name: str, + version: str, + evaluator_version: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.EvaluatorVersion: + """Update an existing EvaluatorVersion with the given version id. - return ItemPaged(get_next, extract_data) + :param name: The name of the resource. Required. + :type name: str + :param version: The version of the EvaluatorVersion to update. Required. + :type version: str + :param evaluator_version: Evaluator resource. Required. + :type evaluator_version: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ @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 - DatasetVersion does not exist. + def update_version( + self, + name: str, + version: str, + evaluator_version: Union[_models.EvaluatorVersion, JSON, IO[bytes]], + **kwargs: Any + ) -> _models.EvaluatorVersion: + """Update an existing EvaluatorVersion with the given version id. :param name: The name of the resource. Required. :type name: str - :param version: The specific version id of the DatasetVersion to retrieve. Required. + :param version: The version of the EvaluatorVersion to update. Required. :type version: str - :return: DatasetVersion. The DatasetVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.DatasetVersion + :param evaluator_version: Evaluator resource. Is one of the following types: EvaluatorVersion, + JSON, IO[bytes] Required. + :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion or JSON or IO[bytes] + :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4041,15 +10098,25 @@ def get(self, name: str, version: str, **kwargs: Any) -> _models.DatasetVersion: } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.DatasetVersion] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) - _request = build_datasets_get_request( + content_type = content_type or "application/json" + _content = None + if isinstance(evaluator_version, (IOBase, bytes)): + _content = evaluator_version + else: + _content = json.dumps(evaluator_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_evaluators_update_version_request( name=name, version=version, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -4078,158 +10145,111 @@ def get(self, name: str, version: str, **kwargs: Any) -> _models.DatasetVersion: if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.DatasetVersion, response.json()) + deserialized = _deserialize(_models.EvaluatorVersion, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @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 - DatasetVersion was deleted successfully or if the DatasetVersion does not exist. - - :param name: The name of the resource. Required. - :type name: str - :param version: The version of the DatasetVersion to delete. Required. - :type version: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[None] = kwargs.pop("cls", None) - - _request = build_datasets_delete_request( - name=name, - version=version, - 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) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [204]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if cls: - return cls(pipeline_response, None, {}) # type: ignore - @overload - def create_or_update( + def pending_upload( self, name: str, version: str, - dataset_version: _models.DatasetVersion, + pending_upload_request: _models.PendingUploadRequest, *, - content_type: str = "application/merge-patch+json", + content_type: str = "application/json", **kwargs: Any - ) -> _models.DatasetVersion: - """Create a new or update an existing DatasetVersion with the given version id. + ) -> _models.PendingUploadResponse: + """Start a new or get an existing pending upload of an evaluator for a specific version. - :param name: The name of the resource. Required. + :param name: Required. :type name: str - :param version: The specific version id of the DatasetVersion to create or update. Required. + :param version: The specific version id of the EvaluatorVersion to operate on. Required. :type version: str - :param dataset_version: The DatasetVersion to create or update. Required. - :type dataset_version: ~azure.ai.projects.models.DatasetVersion + :param pending_upload_request: The pending upload request parameters. Required. + :type pending_upload_request: ~azure.ai.projects.models.PendingUploadRequest :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/merge-patch+json". + Default value is "application/json". :paramtype content_type: str - :return: DatasetVersion. The DatasetVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.DatasetVersion + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def create_or_update( + def pending_upload( self, name: str, version: str, - dataset_version: JSON, + pending_upload_request: JSON, *, - content_type: str = "application/merge-patch+json", + content_type: str = "application/json", **kwargs: Any - ) -> _models.DatasetVersion: - """Create a new or update an existing DatasetVersion with the given version id. + ) -> _models.PendingUploadResponse: + """Start a new or get an existing pending upload of an evaluator for a specific version. - :param name: The name of the resource. Required. + :param name: Required. :type name: str - :param version: The specific version id of the DatasetVersion to create or update. Required. + :param version: The specific version id of the EvaluatorVersion to operate on. Required. :type version: str - :param dataset_version: The DatasetVersion to create or update. Required. - :type dataset_version: JSON + :param pending_upload_request: The pending upload request parameters. Required. + :type pending_upload_request: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/merge-patch+json". + Default value is "application/json". :paramtype content_type: str - :return: DatasetVersion. The DatasetVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.DatasetVersion + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def create_or_update( + def pending_upload( self, name: str, version: str, - dataset_version: IO[bytes], + pending_upload_request: IO[bytes], *, - content_type: str = "application/merge-patch+json", + content_type: str = "application/json", **kwargs: Any - ) -> _models.DatasetVersion: - """Create a new or update an existing DatasetVersion with the given version id. + ) -> _models.PendingUploadResponse: + """Start a new or get an existing pending upload of an evaluator for a specific version. - :param name: The name of the resource. Required. + :param name: Required. :type name: str - :param version: The specific version id of the DatasetVersion to create or update. Required. + :param version: The specific version id of the EvaluatorVersion to operate on. Required. :type version: str - :param dataset_version: The DatasetVersion to create or update. Required. - :type dataset_version: IO[bytes] + :param pending_upload_request: The pending upload request parameters. Required. + :type pending_upload_request: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/merge-patch+json". + Default value is "application/json". :paramtype content_type: str - :return: DatasetVersion. The DatasetVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.DatasetVersion + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - 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. + def pending_upload( + self, + name: str, + version: str, + 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. - :param name: The name of the resource. Required. + :param name: Required. :type name: str - :param version: The specific version id of the DatasetVersion to create or update. Required. + :param version: The specific version id of the EvaluatorVersion to operate on. Required. :type version: str - :param dataset_version: The DatasetVersion to create or update. Is one of the following types: - DatasetVersion, JSON, IO[bytes] Required. - :type dataset_version: ~azure.ai.projects.models.DatasetVersion or JSON or IO[bytes] - :return: DatasetVersion. The DatasetVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.DatasetVersion + :param pending_upload_request: The pending upload request parameters. Is one of the following + types: PendingUploadRequest, JSON, IO[bytes] Required. + :type pending_upload_request: ~azure.ai.projects.models.PendingUploadRequest or JSON or + IO[bytes] + :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.PendingUploadResponse :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4244,16 +10264,16 @@ def create_or_update( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.DatasetVersion] = kwargs.pop("cls", None) + cls: ClsType[_models.PendingUploadResponse] = kwargs.pop("cls", None) - content_type = content_type or "application/merge-patch+json" + content_type = content_type or "application/json" _content = None - if isinstance(dataset_version, (IOBase, bytes)): - _content = dataset_version + if isinstance(pending_upload_request, (IOBase, bytes)): + _content = pending_upload_request else: - _content = json.dumps(dataset_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(pending_upload_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_datasets_create_or_update_request( + _request = build_beta_evaluators_pending_upload_request( name=name, version=version, content_type=content_type, @@ -4275,19 +10295,23 @@ def create_or_update( response = pipeline_response.http_response - if response.status_code not in [200, 201]: + 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) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.DatasetVersion, response.json()) + deserialized = _deserialize(_models.PendingUploadResponse, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -4295,103 +10319,103 @@ def create_or_update( return deserialized # type: ignore @overload - def pending_upload( + def get_credentials( self, name: str, version: str, - pending_upload_request: _models.PendingUploadRequest, + credential_request: _models.EvaluatorCredentialRequest, *, 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. + ) -> _models.DatasetCredential: + """Get the SAS credential to access the storage account associated with an Evaluator version. - :param name: The name of the resource. Required. + :param name: Required. :type name: str - :param version: The specific version id of the DatasetVersion to operate on. Required. + :param version: The specific version id of the EvaluatorVersion to operate on. Required. :type version: str - :param pending_upload_request: The pending upload request parameters. Required. - :type pending_upload_request: ~azure.ai.projects.models.PendingUploadRequest + :param credential_request: The credential request parameters. Required. + :type credential_request: ~azure.ai.projects.models.EvaluatorCredentialRequest :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.PendingUploadResponse + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def pending_upload( + def get_credentials( self, name: str, version: str, - pending_upload_request: JSON, + credential_request: JSON, *, 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. + ) -> _models.DatasetCredential: + """Get the SAS credential to access the storage account associated with an Evaluator version. - :param name: The name of the resource. Required. + :param name: Required. :type name: str - :param version: The specific version id of the DatasetVersion to operate on. Required. + :param version: The specific version id of the EvaluatorVersion to operate on. Required. :type version: str - :param pending_upload_request: The pending upload request parameters. Required. - :type pending_upload_request: JSON + :param credential_request: The credential request parameters. Required. + :type credential_request: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.PendingUploadResponse + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def pending_upload( + def get_credentials( self, name: str, version: str, - pending_upload_request: IO[bytes], + credential_request: IO[bytes], *, 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. + ) -> _models.DatasetCredential: + """Get the SAS credential to access the storage account associated with an Evaluator version. - :param name: The name of the resource. Required. + :param name: Required. :type name: str - :param version: The specific version id of the DatasetVersion to operate on. Required. + :param version: The specific version id of the EvaluatorVersion to operate on. Required. :type version: str - :param pending_upload_request: The pending upload request parameters. Required. - :type pending_upload_request: IO[bytes] + :param credential_request: The credential request parameters. Required. + :type credential_request: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.PendingUploadResponse + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def pending_upload( + def get_credentials( self, name: str, version: str, - pending_upload_request: Union[_models.PendingUploadRequest, JSON, IO[bytes]], + credential_request: Union[_models.EvaluatorCredentialRequest, JSON, IO[bytes]], **kwargs: Any - ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of a dataset for a specific version. + ) -> _models.DatasetCredential: + """Get the SAS credential to access the storage account associated with an Evaluator version. - :param name: The name of the resource. Required. + :param name: Required. :type name: str - :param version: The specific version id of the DatasetVersion to operate on. Required. + :param version: The specific version id of the EvaluatorVersion to operate on. Required. :type version: str - :param pending_upload_request: The pending upload request parameters. Is one of the following - types: PendingUploadRequest, JSON, IO[bytes] Required. - :type pending_upload_request: ~azure.ai.projects.models.PendingUploadRequest or JSON or + :param credential_request: The credential request parameters. Is one of the following types: + EvaluatorCredentialRequest, JSON, IO[bytes] Required. + :type credential_request: ~azure.ai.projects.models.EvaluatorCredentialRequest or JSON or IO[bytes] - :return: PendingUploadResponse. The PendingUploadResponse is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.PendingUploadResponse + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4406,16 +10430,16 @@ def pending_upload( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.PendingUploadResponse] = kwargs.pop("cls", None) + cls: ClsType[_models.DatasetCredential] = kwargs.pop("cls", None) content_type = content_type or "application/json" _content = None - if isinstance(pending_upload_request, (IOBase, bytes)): - _content = pending_upload_request + if isinstance(credential_request, (IOBase, bytes)): + _content = credential_request else: - _content = json.dumps(pending_upload_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(credential_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_datasets_pending_upload_request( + _request = build_beta_evaluators_get_credentials_request( name=name, version=version, content_type=content_type, @@ -4444,28 +10468,119 @@ def pending_upload( except (StreamConsumedError, StreamClosedError): pass map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.PendingUploadResponse, response.json()) + deserialized = _deserialize(_models.DatasetCredential, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + @overload + def create_generation_job( + self, + job: _models.EvaluatorGenerationJob, + *, + operation_id: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.EvaluatorGenerationJob: + """Creates an evaluator generation job. + + Creates an evaluator generation job. The service generates rubric-based evaluator definitions + from the provided source materials asynchronously. + + :param job: The job to create. Required. + :type job: ~azure.ai.projects.models.EvaluatorGenerationJob + :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 + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + Creates an evaluator generation job. The service generates rubric-based evaluator definitions + from the provided source materials asynchronously. + + :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 + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_generation_job( + self, + job: IO[bytes], + *, + operation_id: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.EvaluatorGenerationJob: + """Creates an evaluator generation job. + + Creates an evaluator generation job. The service generates rubric-based evaluator definitions + from the provided source materials asynchronously. + + :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 + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob + :raises ~azure.core.exceptions.HttpResponseError: + """ + @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. + def create_generation_job( + self, + job: Union[_models.EvaluatorGenerationJob, JSON, IO[bytes]], + *, + operation_id: Optional[str] = None, + **kwargs: Any + ) -> _models.EvaluatorGenerationJob: + """Creates an evaluator generation job. - :param name: The name of the resource. Required. - :type name: str - :param version: The specific version id of the DatasetVersion to operate on. Required. - :type version: str - :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.DatasetCredential + Creates an evaluator generation job. The service generates rubric-based evaluator definitions + from the provided source materials asynchronously. + + :param job: The job to create. Is one of the following types: EvaluatorGenerationJob, JSON, + IO[bytes] Required. + :type job: ~azure.ai.projects.models.EvaluatorGenerationJob 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 + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4476,15 +10591,24 @@ def get_credentials(self, name: str, version: str, **kwargs: Any) -> _models.Dat } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.DatasetCredential] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.EvaluatorGenerationJob] = kwargs.pop("cls", None) - _request = build_datasets_get_credentials_request( - name=name, - version=version, + content_type = content_type or "application/json" + _content = None + if isinstance(job, (IOBase, bytes)): + _content = job + else: + _content = json.dumps(job, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_evaluators_create_generation_job_request( + operation_id=operation_id, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -4501,51 +10625,43 @@ def get_credentials(self, name: str, version: str, **kwargs: Any) -> _models.Dat response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [201]: 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) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) + response_headers["Location"] = self._deserialize("str", response.headers.get("Location")) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.DatasetCredential, response.json()) + deserialized = _deserialize(_models.EvaluatorGenerationJob, response.json()) if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, deserialized, response_headers) # type: ignore return deserialized # type: ignore - -class DeploymentsOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`deployments` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - @distributed_trace - def get(self, name: str, **kwargs: Any) -> _models.Deployment: - """Get a deployed model. + def get_generation_job(self, job_id: str, **kwargs: Any) -> _models.EvaluatorGenerationJob: + """Get info about an evaluator generation job. - :param name: Name of the deployment. Required. - :type name: str - :return: Deployment. The Deployment is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Deployment + Gets the details of an evaluator generation job by its ID. + + :param job_id: The ID of the job. Required. + :type job_id: str + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4559,10 +10675,10 @@ def get(self, name: str, **kwargs: Any) -> _models.Deployment: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.Deployment] = kwargs.pop("cls", None) + cls: ClsType[_models.EvaluatorGenerationJob] = kwargs.pop("cls", None) - _request = build_deployments_get_request( - name=name, + _request = build_beta_evaluators_get_generation_job_request( + job_id=job_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -4587,158 +10703,60 @@ def get(self, name: str, **kwargs: Any) -> _models.Deployment: except (StreamConsumedError, StreamClosedError): pass map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) response_headers = {} - response_headers["x-ms-client-request-id"] = self._deserialize( - "str", response.headers.get("x-ms-client-request-id") - ) + response_headers["Retry-After"] = self._deserialize("int", response.headers.get("Retry-After")) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.Deployment, response.json()) - - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore - - @distributed_trace - def list( - self, - *, - model_publisher: Optional[str] = None, - model_name: Optional[str] = None, - deployment_type: Optional[Union[str, _models.DeploymentType]] = None, - **kwargs: Any - ) -> ItemPaged["_models.Deployment"]: - """List all deployed models in the project. - - :keyword model_publisher: Model publisher to filter models by. Default value is None. - :paramtype model_publisher: str - :keyword model_name: Model name (the publisher specific name) to filter models by. Default - value is None. - :paramtype model_name: str - :keyword deployment_type: Type of deployment to filter list by. "ModelDeployment" Default value - is None. - :paramtype deployment_type: str or ~azure.ai.projects.models.DeploymentType - :return: An iterator like instance of Deployment - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Deployment] - :raises ~azure.core.exceptions.HttpResponseError: - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[List[_models.Deployment]] = kwargs.pop("cls", None) - - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_deployments_list_request( - model_publisher=model_publisher, - model_name=model_name, - deployment_type=deployment_type, - 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) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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 - - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.Deployment], - deserialized.get("value", []), - ) - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) - - def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return ItemPaged(get_next, extract_data) - - -class IndexesOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`indexes` attribute. - """ + deserialized = _deserialize(_models.EvaluatorGenerationJob, response.json()) - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore @distributed_trace - def list_versions(self, name: str, **kwargs: Any) -> ItemPaged["_models.Index"]: - """List all versions of the given Index. + def list_generation_jobs( + self, + *, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_models.EvaluatorGenerationJob"]: + """Returns a list of evaluator generation jobs. - :param name: The name of the resource. Required. - :type name: str - :return: An iterator like instance of Index - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Index] + Returns a list of evaluator generation jobs. + + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of EvaluatorGenerationJob + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.EvaluatorGenerationJob] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.Index]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.EvaluatorGenerationJob]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -4748,56 +10766,35 @@ def list_versions(self, name: str, **kwargs: Any) -> ItemPaged["_models.Index"]: } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(next_link=None): - if not next_link: - - _request = build_indexes_list_versions_request( - name=name, - 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) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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) + def prepare_request(_continuation_token=None): + _request = build_beta_evaluators_list_generation_jobs_request( + 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 def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.Index], - deserialized.get("value", []), + List[_models.EvaluatorGenerationJob], + deserialized.get("data", []), ) if cls: list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) + return deserialized.get("last_id") or None, iter(list_of_elem) - def get_next(next_link=None): - _request = prepare_request(next_link) + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) _stream = False pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access @@ -4807,25 +10804,96 @@ def get_next(next_link=None): if response.status_code not in [200]: map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) return pipeline_response return ItemPaged(get_next, extract_data) @distributed_trace - def list(self, **kwargs: Any) -> ItemPaged["_models.Index"]: - """List the latest version of each Index. + def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.EvaluatorGenerationJob: + """Cancels an evaluator generation job. - :return: An iterator like instance of Index - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Index] + Cancels an evaluator generation job by its ID. + + :param job_id: The ID of the job to cancel. Required. + :type job_id: str + :return: EvaluatorGenerationJob. The EvaluatorGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.EvaluatorGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.Index]] = kwargs.pop("cls", None) + cls: ClsType[_models.EvaluatorGenerationJob] = kwargs.pop("cls", None) + + _request = build_beta_evaluators_cancel_generation_job_request( + job_id=job_id, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.EvaluatorGenerationJob, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + return deserialized # type: ignore + + @distributed_trace + 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 + evaluator (if any) is preserved. + + :param job_id: The ID of the job to delete. Required. + :type job_id: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -4834,81 +10902,114 @@ def list(self, **kwargs: Any) -> ItemPaged["_models.Index"]: } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(next_link=None): - if not next_link: + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - _request = build_indexes_list_request( - 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) + cls: ClsType[None] = kwargs.pop("cls", None) - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_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_evaluators_delete_generation_job_request( + job_id=job_id, + 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 + _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.Index], - deserialized.get("value", []), + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, ) - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) + raise HttpResponseError(response=response, model=error) - def get_next(next_link=None): - _request = prepare_request(next_link) + if cls: + return cls(pipeline_response, None, {}) # type: ignore + + +class BetaInsightsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`insights` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @overload + def generate( + self, insight: _models.Insight, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.Insight: + """Generate Insights. + + :param insight: Complete evaluation configuration including data source, evaluators, and result + settings. Required. + :type insight: ~azure.ai.projects.models.Insight + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: Insight. The Insight is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Insight + :raises ~azure.core.exceptions.HttpResponseError: + """ - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response + @overload + def generate(self, insight: JSON, *, content_type: str = "application/json", **kwargs: Any) -> _models.Insight: + """Generate Insights. - if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + :param insight: Complete evaluation configuration including data source, evaluators, and result + settings. Required. + :type insight: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: Insight. The Insight is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Insight + :raises ~azure.core.exceptions.HttpResponseError: + """ - return pipeline_response + @overload + def generate(self, insight: IO[bytes], *, content_type: str = "application/json", **kwargs: Any) -> _models.Insight: + """Generate Insights. - return ItemPaged(get_next, extract_data) + :param insight: Complete evaluation configuration including data source, evaluators, and result + settings. Required. + :type insight: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: Insight. The Insight is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Insight + :raises ~azure.core.exceptions.HttpResponseError: + """ @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 - does not exist. + def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwargs: Any) -> _models.Insight: + """Generate Insights. - :param name: The name of the resource. Required. - :type name: str - :param version: The specific version id of the Index to retrieve. Required. - :type version: str - :return: Index. The Index is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Index + :param insight: Complete evaluation configuration including data source, evaluators, and result + settings. Is one of the following types: Insight, JSON, IO[bytes] Required. + :type insight: ~azure.ai.projects.models.Insight or JSON or IO[bytes] + :return: Insight. The Insight is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Insight :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4919,15 +11020,23 @@ def get(self, name: str, version: str, **kwargs: Any) -> _models.Index: } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.Index] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.Insight] = kwargs.pop("cls", None) - _request = build_indexes_get_request( - name=name, - version=version, + content_type = content_type or "application/json" + _content = None + if isinstance(insight, (IOBase, bytes)): + _content = insight + else: + _content = json.dumps(insight, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_insights_generate_request( + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -4944,19 +11053,23 @@ def get(self, name: str, version: str, **kwargs: Any) -> _models.Index: response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [201]: 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) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.Index, response.json()) + deserialized = _deserialize(_models.Insight, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -4964,16 +11077,16 @@ def get(self, name: str, version: str, **kwargs: Any) -> _models.Index: return deserialized # type: ignore @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 - deleted successfully or if the Index does not exist. + def get(self, insight_id: str, *, include_coordinates: Optional[bool] = None, **kwargs: Any) -> _models.Insight: + """Get a specific insight by Id. - :param name: The name of the resource. Required. - :type name: str - :param version: The version of the Index to delete. Required. - :type version: str - :return: None - :rtype: None + :param insight_id: The unique identifier for the insights report. Required. + :type insight_id: str + :keyword include_coordinates: Whether to include coordinates for visualization in the response. + Defaults to false. Default value is None. + :paramtype include_coordinates: bool + :return: Insight. The Insight is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Insight :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -4987,11 +11100,11 @@ def delete(self, name: str, version: str, **kwargs: Any) -> None: # pylint: dis _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[None] = kwargs.pop("cls", None) + cls: ClsType[_models.Insight] = kwargs.pop("cls", None) - _request = build_indexes_delete_request( - name=name, - version=version, + _request = build_beta_insights_get_request( + insight_id=insight_id, + include_coordinates=include_coordinates, api_version=self._config.api_version, headers=_headers, params=_params, @@ -5001,109 +11114,71 @@ def delete(self, name: str, version: str, **kwargs: Any) -> None: # pylint: dis } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _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 ) response = pipeline_response.http_response - if response.status_code not in [204]: + 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) - raise HttpResponseError(response=response) - - if cls: - return cls(pipeline_response, None, {}) # type: ignore - - @overload - def create_or_update( - self, - name: str, - version: str, - index: _models.Index, - *, - content_type: str = "application/merge-patch+json", - **kwargs: Any - ) -> _models.Index: - """Create a new or update an existing Index with the given version id. + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) - :param name: The name of the resource. Required. - :type name: str - :param version: The specific version id of the Index to create or update. Required. - :type version: str - :param index: The Index to create or update. Required. - :type index: ~azure.ai.projects.models.Index - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/merge-patch+json". - :paramtype content_type: str - :return: Index. The Index is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Index - :raises ~azure.core.exceptions.HttpResponseError: - """ + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Insight, response.json()) - @overload - 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. + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore - :param name: The name of the resource. Required. - :type name: str - :param version: The specific version id of the Index to create or update. Required. - :type version: str - :param index: The Index to create or update. Required. - :type index: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/merge-patch+json". - :paramtype content_type: str - :return: Index. The Index is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Index - :raises ~azure.core.exceptions.HttpResponseError: - """ + return deserialized # type: ignore - @overload - def create_or_update( + @distributed_trace + def list( self, - name: str, - version: str, - index: IO[bytes], *, - content_type: str = "application/merge-patch+json", + type: Optional[Union[str, _models.InsightType]] = None, + eval_id: Optional[str] = None, + run_id: Optional[str] = None, + agent_name: Optional[str] = None, + include_coordinates: Optional[bool] = None, **kwargs: Any - ) -> _models.Index: - """Create a new or update an existing Index with the given version id. + ) -> ItemPaged["_models.Insight"]: + """List all insights in reverse chronological order (newest first). - :param name: The name of the resource. Required. - :type name: str - :param version: The specific version id of the Index to create or update. Required. - :type version: str - :param index: The Index to create or update. Required. - :type index: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/merge-patch+json". - :paramtype content_type: str - :return: Index. The Index is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Index + :keyword type: Filter by the type of analysis. Known values are: "EvaluationRunClusterInsight", + "AgentClusterInsight", and "EvaluationComparison". Default value is None. + :paramtype type: str or ~azure.ai.projects.models.InsightType + :keyword eval_id: Filter by the evaluation ID. Default value is None. + :paramtype eval_id: str + :keyword run_id: Filter by the evaluation run ID. Default value is None. + :paramtype run_id: str + :keyword agent_name: Filter by the agent name. Default value is None. + :paramtype agent_name: str + :keyword include_coordinates: Whether to include coordinates for visualization in the response. + Defaults to false. Default value is None. + :paramtype include_coordinates: bool + :return: An iterator like instance of Insight + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Insight] :raises ~azure.core.exceptions.HttpResponseError: """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - @distributed_trace - 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. + cls: ClsType[List[_models.Insight]] = kwargs.pop("cls", None) - :param name: The name of the resource. Required. - :type name: str - :param version: The specific version id of the Index to create or update. Required. - :type version: str - :param index: The Index to create or update. Is one of the following types: Index, JSON, - IO[bytes] Required. - :type index: ~azure.ai.projects.models.Index or JSON or IO[bytes] - :return: Index. The Index is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Index - :raises ~azure.core.exceptions.HttpResponseError: - """ error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -5112,69 +11187,91 @@ def create_or_update( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} + def prepare_request(next_link=None): + if not next_link: - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.Index] = kwargs.pop("cls", None) + _request = build_beta_insights_list_request( + type=type, + eval_id=eval_id, + run_id=run_id, + agent_name=agent_name, + include_coordinates=include_coordinates, + 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) - content_type = content_type or "application/merge-patch+json" - _content = None - if isinstance(index, (IOBase, bytes)): - _content = index - else: - _content = json.dumps(index, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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_indexes_create_or_update_request( - name=name, - version=version, - content_type=content_type, - api_version=self._config.api_version, - content=_content, - 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.Insight], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) - response = pipeline_response.http_response + def get_next(next_link=None): + _request = prepare_request(next_link) - if response.status_code not in [200, 201]: - 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) - raise HttpResponseError(response=response) + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.Index, 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) -class BetaAgentsOperations: +class BetaMemoryStoresOperations: """ .. warning:: **DO NOT** instantiate this class directly. Instead, you should access the following operations through :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`agents` attribute. + :attr:`memory_stores` attribute. """ def __init__(self, *args, **kwargs) -> None: @@ -5185,89 +11282,93 @@ def __init__(self, *args, **kwargs) -> None: self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") @overload - def patch_agent_details( + def create( self, - agent_name: str, *, - content_type: str = "application/merge-patch+json", - agent_endpoint: Optional[_models.AgentEndpoint] = None, - agent_card: Optional[_models.AgentCard] = None, + name: str, + definition: _models.MemoryStoreDefinition, + content_type: str = "application/json", + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, **kwargs: Any - ) -> _models.AgentDetails: - """Updates an agent endpoint. + ) -> _models.MemoryStoreDetails: + """Create a memory store. - :param agent_name: The name of the agent to retrieve. Required. - :type agent_name: str + :keyword name: The name of the memory store. Required. + :paramtype name: str + :keyword definition: The memory store definition. Required. + :paramtype definition: ~azure.ai.projects.models.MemoryStoreDefinition :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/merge-patch+json". + Default value is "application/json". :paramtype content_type: str - :keyword agent_endpoint: The endpoint configuration for the agent. Default value is None. - :paramtype agent_endpoint: ~azure.ai.projects.models.AgentEndpoint - :keyword agent_card: Optional agent card for the agent. Default value is None. - :paramtype agent_card: ~azure.ai.projects.models.AgentCard - :return: AgentDetails. The AgentDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentDetails + :keyword description: A human-readable description of the memory store. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default + value is None. + :paramtype metadata: dict[str, str] + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + def create( + self, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Create a memory store. - :param agent_name: The name of the agent to retrieve. Required. - :type agent_name: str :param body: Required. :type body: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/merge-patch+json". + Default value is "application/json". :paramtype content_type: str - :return: AgentDetails. The AgentDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentDetails + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + def create( + self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Create a memory store. - :param agent_name: The name of the agent to retrieve. Required. - :type agent_name: str :param body: Required. :type body: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/merge-patch+json". + Default value is "application/json". :paramtype content_type: str - :return: AgentDetails. The AgentDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentDetails + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def patch_agent_details( + def create( self, - agent_name: str, body: Union[JSON, IO[bytes]] = _Unset, *, - agent_endpoint: Optional[_models.AgentEndpoint] = None, - agent_card: Optional[_models.AgentCard] = None, + name: str = _Unset, + definition: _models.MemoryStoreDefinition = _Unset, + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, **kwargs: Any - ) -> _models.AgentDetails: - """Updates an agent endpoint. + ) -> _models.MemoryStoreDetails: + """Create a memory store. - :param agent_name: The name of the agent to retrieve. Required. - :type agent_name: str :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] - :keyword agent_endpoint: The endpoint configuration for the agent. Default value is None. - :paramtype agent_endpoint: ~azure.ai.projects.models.AgentEndpoint - :keyword agent_card: Optional agent card for the agent. Default value is None. - :paramtype agent_card: ~azure.ai.projects.models.AgentCard - :return: AgentDetails. The AgentDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentDetails + :keyword name: The name of the memory store. Required. + :paramtype name: str + :keyword definition: The memory store definition. Required. + :paramtype definition: ~azure.ai.projects.models.MemoryStoreDefinition + :keyword description: A human-readable description of the memory store. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default + value is None. + :paramtype metadata: dict[str, str] + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -5282,20 +11383,23 @@ def patch_agent_details( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.AgentDetails] = kwargs.pop("cls", None) + cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) if body is _Unset: - body = {"agent_card": agent_card, "agent_endpoint": agent_endpoint} + if name is _Unset: + raise TypeError("missing required argument: name") + if definition is _Unset: + raise TypeError("missing required argument: definition") + body = {"definition": definition, "description": description, "metadata": metadata, "name": name} body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/merge-patch+json" + content_type = content_type or "application/json" _content = None if isinstance(body, (IOBase, bytes)): _content = body else: _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_agents_patch_agent_details_request( - agent_name=agent_name, + _request = build_beta_memory_stores_create_request( content_type=content_type, api_version=self._config.api_version, content=_content, @@ -5331,7 +11435,7 @@ def patch_agent_details( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.AgentDetails, response.json()) + deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -5339,119 +11443,91 @@ def patch_agent_details( return deserialized # type: ignore @overload - def create_session( + def update( self, - agent_name: str, + name: str, *, - isolation_key: str, - version_indicator: _models.VersionIndicator, content_type: str = "application/json", - agent_session_id: Optional[str] = None, + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, **kwargs: Any - ) -> _models.AgentSessionResource: - """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. + ) -> _models.MemoryStoreDetails: + """Update a memory store. - :param agent_name: The name of the agent to create a session for. Required. - :type agent_name: str - :keyword isolation_key: Isolation key used by the agent endpoint to enforce session ownership - for session-mutating operations. Required. - :paramtype isolation_key: str - :keyword version_indicator: Determines which agent version backs the session. Required. - :paramtype version_indicator: ~azure.ai.projects.models.VersionIndicator + :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. Default value is "application/json". :paramtype content_type: str - :keyword agent_session_id: Optional caller-provided session ID. If specified, it must be unique - within the agent endpoint. Auto-generated if omitted. Default value is None. - :paramtype agent_session_id: str - :return: AgentSessionResource. The AgentSessionResource is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentSessionResource + :keyword description: A human-readable description of the memory store. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default + value is None. + :paramtype metadata: dict[str, str] + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def create_session( - self, agent_name: str, body: JSON, *, isolation_key: str, content_type: str = "application/json", **kwargs: Any - ) -> _models.AgentSessionResource: - """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. + def update( + self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Update a memory store. - :param agent_name: The name of the agent to create a session for. Required. - :type agent_name: str + :param name: The name of the memory store to update. Required. + :type name: str :param body: Required. :type body: JSON - :keyword isolation_key: Isolation key used by the agent endpoint to enforce session ownership - for session-mutating operations. Required. - :paramtype isolation_key: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: AgentSessionResource. The AgentSessionResource is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentSessionResource + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def create_session( - self, - agent_name: str, - body: IO[bytes], - *, - isolation_key: str, - content_type: str = "application/json", - **kwargs: Any - ) -> _models.AgentSessionResource: - """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. + def update( + self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreDetails: + """Update a memory store. - :param agent_name: The name of the agent to create a session for. Required. - :type agent_name: str + :param name: The name of the memory store to update. Required. + :type name: str :param body: Required. :type body: IO[bytes] - :keyword isolation_key: Isolation key used by the agent endpoint to enforce session ownership - for session-mutating operations. Required. - :paramtype isolation_key: str :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: AgentSessionResource. The AgentSessionResource is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentSessionResource + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def create_session( + def update( self, - agent_name: str, + name: str, body: Union[JSON, IO[bytes]] = _Unset, *, - isolation_key: str, - version_indicator: _models.VersionIndicator = _Unset, - agent_session_id: Optional[str] = None, + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, **kwargs: Any - ) -> _models.AgentSessionResource: - """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. + ) -> _models.MemoryStoreDetails: + """Update a memory store. - :param agent_name: The name of the agent to create a session for. Required. - :type agent_name: str + :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. :type body: JSON or IO[bytes] - :keyword isolation_key: Isolation key used by the agent endpoint to enforce session ownership - for session-mutating operations. Required. - :paramtype isolation_key: str - :keyword version_indicator: Determines which agent version backs the session. Required. - :paramtype version_indicator: ~azure.ai.projects.models.VersionIndicator - :keyword agent_session_id: Optional caller-provided session ID. If specified, it must be unique - within the agent endpoint. Auto-generated if omitted. Default value is None. - :paramtype agent_session_id: str - :return: AgentSessionResource. The AgentSessionResource is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentSessionResource + :keyword description: A human-readable description of the memory store. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default + value is None. + :paramtype metadata: dict[str, str] + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -5466,12 +11542,10 @@ def create_session( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.AgentSessionResource] = kwargs.pop("cls", None) + cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) if body is _Unset: - if version_indicator is _Unset: - raise TypeError("missing required argument: version_indicator") - body = {"agent_session_id": agent_session_id, "version_indicator": version_indicator} + body = {"description": description, "metadata": metadata} body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None @@ -5480,9 +11554,8 @@ def create_session( else: _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_agents_create_session_request( - agent_name=agent_name, - isolation_key=isolation_key, + _request = build_beta_memory_stores_update_request( + name=name, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -5502,7 +11575,7 @@ def create_session( response = pipeline_response.http_response - if response.status_code not in [201]: + if response.status_code not in [200]: if _stream: try: response.read() # Load the body in memory and close the socket @@ -5518,7 +11591,7 @@ def create_session( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.AgentSessionResource, response.json()) + deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -5526,15 +11599,13 @@ def create_session( return deserialized # type: ignore @distributed_trace - def get_session(self, agent_name: str, session_id: str, **kwargs: Any) -> _models.AgentSessionResource: - """Retrieves a session by ID. + def get(self, name: str, **kwargs: Any) -> _models.MemoryStoreDetails: + """Retrieve a memory store. - :param agent_name: The name of the agent. Required. - :type agent_name: str - :param session_id: The session identifier. Required. - :type session_id: str - :return: AgentSessionResource. The AgentSessionResource is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentSessionResource + :param name: The name of the memory store to retrieve. Required. + :type name: str + :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDetails :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -5548,11 +11619,10 @@ def get_session(self, agent_name: str, session_id: str, **kwargs: Any) -> _model _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.AgentSessionResource] = kwargs.pop("cls", None) + cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) - _request = build_beta_agents_get_session_request( - agent_name=agent_name, - session_id=session_id, + _request = build_beta_memory_stores_get_request( + name=name, api_version=self._config.api_version, headers=_headers, params=_params, @@ -5586,7 +11656,7 @@ def get_session(self, agent_name: str, session_id: str, **kwargs: Any) -> _model if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.AgentSessionResource, response.json()) + deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -5594,81 +11664,16 @@ def get_session(self, agent_name: str, session_id: str, **kwargs: Any) -> _model return deserialized # type: ignore @distributed_trace - def delete_session( # pylint: disable=inconsistent-return-statements - self, agent_name: str, session_id: str, *, isolation_key: str, **kwargs: Any - ) -> None: - """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. - :type agent_name: str - :param session_id: The session identifier. Required. - :type session_id: str - :keyword isolation_key: Isolation key used by the agent endpoint to enforce session ownership - for session-mutating operations. Required. - :paramtype isolation_key: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[None] = kwargs.pop("cls", None) - - _request = build_beta_agents_delete_session_request( - agent_name=agent_name, - session_id=session_id, - isolation_key=isolation_key, - 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) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [204]: - 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, None, {}) # type: ignore - - @distributed_trace - def list_sessions( + def list( self, - agent_name: str, *, limit: Optional[int] = None, order: Optional[Union[str, _models.PageOrder]] = None, before: Optional[str] = None, **kwargs: Any - ) -> ItemPaged["_models.AgentSessionResource"]: - """Returns a list of sessions for the specified agent. + ) -> ItemPaged["_models.MemoryStoreDetails"]: + """List all memory stores. - :param agent_name: The name of the agent. Required. - :type agent_name: str :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. Default value is None. @@ -5683,14 +11688,14 @@ def list_sessions( 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: An iterator like instance of AgentSessionResource - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.AgentSessionResource] + :return: An iterator like instance of MemoryStoreDetails + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.MemoryStoreDetails] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.AgentSessionResource]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.MemoryStoreDetails]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -5702,8 +11707,7 @@ def list_sessions( def prepare_request(_continuation_token=None): - _request = build_beta_agents_list_sessions_request( - agent_name=agent_name, + _request = build_beta_memory_stores_list_request( limit=limit, order=order, after=_continuation_token, @@ -5721,7 +11725,7 @@ def prepare_request(_continuation_token=None): def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.AgentSessionResource], + List[_models.MemoryStoreDetails], deserialized.get("data", []), ) if cls: @@ -5750,44 +11754,121 @@ def get_next(_continuation_token=None): return ItemPaged(get_next, extract_data) @distributed_trace - 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 - as a Server-Sent Events (SSE) stream. + def delete(self, name: str, **kwargs: Any) -> _models.DeleteMemoryStoreResult: + """Delete a memory store. - Each SSE frame contains: + :param name: The name of the memory store to delete. Required. + :type name: str + :return: DeleteMemoryStoreResult. The DeleteMemoryStoreResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DeleteMemoryStoreResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) - * `event`: always `"log"` - * `data`: a plain-text log line (currently JSON-formatted, but the schema is not contractual and may include additional keys or change format over time; clients should treat it as an opaque string) + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - Example SSE frames: + cls: ClsType[_models.DeleteMemoryStoreResult] = kwargs.pop("cls", None) - .. code-block:: + _request = build_beta_memory_stores_delete_request( + name=name, + 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) - event: log - data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting FoundryCBAgent server on port 8088"} + _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 + ) - event: log - data: {"timestamp":"2026-03-10T09:33:17.130Z","stream":"stderr","message":"INFO: Application startup complete."} + response = pipeline_response.http_response - event: log - data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully connected to container"} + 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, + ) + raise HttpResponseError(response=response, model=error) - event: log - data: {"timestamp":"2026-03-10T09:35:52.714Z","stream":"status","message":"No logs since last 60 seconds"} + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DeleteMemoryStoreResult, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + def _search_memories( + self, + name: str, + *, + scope: str, + content_type: str = "application/json", + items: Optional[List[dict[str, Any]]] = None, + previous_search_id: Optional[str] = None, + options: Optional[_models.MemorySearchOptions] = None, + **kwargs: Any + ) -> _models.MemoryStoreSearchResult: ... + @overload + def _search_memories( + self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreSearchResult: ... + @overload + def _search_memories( + self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.MemoryStoreSearchResult: ... - The stream remains open until the client disconnects or the server - terminates the connection. Clients should handle reconnection as needed. + @distributed_trace + def _search_memories( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + scope: str = _Unset, + items: Optional[List[dict[str, Any]]] = None, + previous_search_id: Optional[str] = None, + options: Optional[_models.MemorySearchOptions] = None, + **kwargs: Any + ) -> _models.MemoryStoreSearchResult: + """Search for relevant memories from a memory store based on conversation context. - :param agent_name: The name of the hosted agent. Required. - :type agent_name: str - :param agent_version: The version of the agent. Required. - :type agent_version: str - :param session_id: The session ID (maps to an ADC sandbox). Required. - :type session_id: str - :return: SessionLogEvent. The SessionLogEvent is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SessionLogEvent + :param name: The name of the memory store to search. Required. + :type name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :paramtype scope: str + :keyword items: Items for which to search for relevant memories. Default value is None. + :paramtype items: list[dict[str, any]] + :keyword previous_search_id: The unique ID of the previous search request, enabling incremental + memory search from where the last operation left off. Default value is None. + :paramtype previous_search_id: str + :keyword options: Memory search options. Default value is None. + :paramtype options: ~azure.ai.projects.models.MemorySearchOptions + :return: MemoryStoreSearchResult. The MemoryStoreSearchResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreSearchResult :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -5798,16 +11879,34 @@ def get_session_log_stream( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.SessionLogEvent] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryStoreSearchResult] = kwargs.pop("cls", None) - _request = build_beta_agents_get_session_log_stream_request( - agent_name=agent_name, - agent_version=agent_version, - session_id=session_id, + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = { + "items": items, + "options": options, + "previous_search_id": previous_search_id, + "scope": scope, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_memory_stores_search_memories_request( + name=name, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -5817,7 +11916,7 @@ def get_session_log_stream( _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = True + _stream = kwargs.pop("stream", False) pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) @@ -5837,40 +11936,27 @@ def get_session_log_stream( ) raise HttpResponseError(response=response, model=error) - response_headers = {} - response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) - if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.SessionLogEvent, response.text()) + deserialized = _deserialize(_models.MemoryStoreSearchResult, response.json()) if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore + return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @distributed_trace - def _upload_session_file( - self, agent_name: str, agent_session_id: str, content: bytes, *, path: str, **kwargs: Any - ) -> _models.SessionFileWriteResponse: - """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. - - :param agent_name: The name of the agent. Required. - :type agent_name: str - :param agent_session_id: The session ID. Required. - :type agent_session_id: str - :param content: Required. - :type content: bytes - :keyword path: The destination file path within the sandbox, relative to the session home - directory. Required. - :paramtype path: str - :return: SessionFileWriteResponse. The SessionFileWriteResponse is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.SessionFileWriteResponse - :raises ~azure.core.exceptions.HttpResponseError: - """ + def _update_memories_initial( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + scope: str = _Unset, + items: Optional[List[dict[str, Any]]] = None, + previous_update_id: Optional[str] = None, + update_delay: Optional[int] = None, + **kwargs: Any + ) -> Iterator[bytes]: error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -5882,15 +11968,28 @@ def _upload_session_file( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: str = kwargs.pop("content_type", _headers.pop("Content-Type", "application/octet-stream")) - cls: ClsType[_models.SessionFileWriteResponse] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[Iterator[bytes]] = kwargs.pop("cls", None) - _content = content + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = { + "items": items, + "previous_update_id": previous_update_id, + "scope": scope, + "update_delay": update_delay, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_agents_upload_session_file_request( - agent_name=agent_name, - agent_session_id=agent_session_id, - path=path, + _request = build_beta_memory_stores_update_memories_request( + name=name, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -5903,19 +12002,18 @@ def _upload_session_file( _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = True pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - if response.status_code not in [201]: - if _stream: - try: - response.read() # Load the body in memory and close the socket - except (StreamConsumedError, StreamClosedError): - pass + if response.status_code not in [202]: + 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, @@ -5923,103 +12021,209 @@ def _upload_session_file( ) raise HttpResponseError(response=response, model=error) - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.SessionFileWriteResponse, response.json()) + response_headers = {} + response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, deserialized, response_headers) # type: ignore return deserialized # type: ignore + @overload + def _begin_update_memories( + self, + name: str, + *, + scope: str, + content_type: str = "application/json", + items: Optional[List[dict[str, Any]]] = None, + previous_update_id: Optional[str] = None, + update_delay: Optional[int] = None, + **kwargs: Any + ) -> LROPoller[_models.MemoryStoreUpdateCompletedResult]: ... + @overload + def _begin_update_memories( + self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> LROPoller[_models.MemoryStoreUpdateCompletedResult]: ... + @overload + def _begin_update_memories( + self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> LROPoller[_models.MemoryStoreUpdateCompletedResult]: ... + @distributed_trace - def download_session_file( - self, agent_name: str, agent_session_id: str, *, path: str, **kwargs: Any - ) -> Iterator[bytes]: - """Download a file from the session sandbox as a binary stream. + def _begin_update_memories( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + scope: str = _Unset, + items: Optional[List[dict[str, Any]]] = None, + previous_update_id: Optional[str] = None, + update_delay: Optional[int] = None, + **kwargs: Any + ) -> LROPoller[_models.MemoryStoreUpdateCompletedResult]: + """Update memory store with conversation memories. - :param agent_name: The name of the agent. Required. - :type agent_name: str - :param agent_session_id: The session ID. Required. - :type agent_session_id: str - :keyword path: The file path to download from the sandbox, relative to the session home - directory. Required. - :paramtype path: str - :return: Iterator[bytes] - :rtype: Iterator[bytes] + :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. + :type body: JSON or IO[bytes] + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :paramtype scope: str + :keyword items: Conversation items to be stored in memory. Default value is None. + :paramtype items: list[dict[str, any]] + :keyword previous_update_id: The unique ID of the previous update request, enabling incremental + memory updates from where the last operation left off. Default value is None. + :paramtype previous_update_id: str + :keyword update_delay: Timeout period before processing the memory update in seconds. + If a new update request is received during this period, it will cancel the current request and + reset the timeout. + Set to 0 to immediately trigger the update without delay. + Defaults to 300 (5 minutes). Default value is None. + :paramtype update_delay: int + :return: An instance of LROPoller that returns MemoryStoreUpdateCompletedResult. The + MemoryStoreUpdateCompletedResult is compatible with MutableMapping + :rtype: + ~azure.core.polling.LROPoller[~azure.ai.projects.models.MemoryStoreUpdateCompletedResult] :raises ~azure.core.exceptions.HttpResponseError: """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryStoreUpdateCompletedResult] = kwargs.pop("cls", None) + polling: Union[bool, PollingMethod] = kwargs.pop("polling", True) + lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) + cont_token: Optional[str] = kwargs.pop("continuation_token", None) + if cont_token is None: + raw_result = self._update_memories_initial( + name=name, + body=body, + scope=scope, + items=items, + previous_update_id=previous_update_id, + update_delay=update_delay, + content_type=content_type, + cls=lambda x, y, z: x, + headers=_headers, + params=_params, + **kwargs + ) + raw_result.http_response.read() # type: ignore + kwargs.pop("error_map", None) - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} + def get_long_running_output(pipeline_response): + response_headers = {} + response = pipeline_response.http_response + response_headers["Operation-Location"] = self._deserialize( + "str", response.headers.get("Operation-Location") + ) - cls: ClsType[Iterator[bytes]] = kwargs.pop("cls", None) + deserialized = _deserialize(_models.MemoryStoreUpdateCompletedResult, response.json().get("result", {})) + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + return deserialized - _request = build_beta_agents_download_session_file_request( - agent_name=agent_name, - agent_session_id=agent_session_id, - path=path, - 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) - _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", True) - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs + if polling is True: + polling_method: PollingMethod = cast( + PollingMethod, LROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) + ) + elif polling is False: + polling_method = cast(PollingMethod, NoPolling()) + else: + polling_method = polling + if cont_token: + return LROPoller[_models.MemoryStoreUpdateCompletedResult].from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output, + ) + return LROPoller[_models.MemoryStoreUpdateCompletedResult]( + self._client, raw_result, get_long_running_output, polling_method # type: ignore ) - response = pipeline_response.http_response + @overload + 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. - 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, - ) - raise HttpResponseError(response=response, model=error) + :param name: The name of the memory store. Required. + :type name: str + :keyword scope: The namespace that logically groups and isolates memories to delete, such as a + user ID. Required. + :paramtype scope: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :raises ~azure.core.exceptions.HttpResponseError: + """ - deserialized = response.iter_bytes() if _decompress else response.iter_raw() + @overload + 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. - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + :param name: The name of the memory store. Required. + :type name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :raises ~azure.core.exceptions.HttpResponseError: + """ - return deserialized # type: ignore + @overload + 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. + + :param name: The name of the memory store. Required. + :type name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :raises ~azure.core.exceptions.HttpResponseError: + """ @distributed_trace - def get_session_files( - self, agent_name: str, agent_session_id: str, *, path: str, **kwargs: Any - ) -> _models.SessionDirectoryListResponse: - """List files and directories at a given path in the session sandbox. Returns only the immediate - children of the specified directory (non-recursive). + 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. - :param agent_name: The name of the agent. Required. - :type agent_name: str - :param agent_session_id: The session ID. Required. - :type agent_session_id: str - :keyword path: The directory path to list, relative to the session home directory. Required. - :paramtype path: str - :return: SessionDirectoryListResponse. The SessionDirectoryListResponse is compatible with + :param name: The name of the memory store. Required. + :type name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword scope: The namespace that logically groups and isolates memories to delete, such as a + user ID. Required. + :paramtype scope: str + :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SessionDirectoryListResponse + :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -6030,16 +12234,29 @@ def get_session_files( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.SessionDirectoryListResponse] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryStoreDeleteScopeResult] = kwargs.pop("cls", None) - _request = build_beta_agents_get_session_files_request( - agent_name=agent_name, - agent_session_id=agent_session_id, - path=path, + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = {"scope": scope} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_memory_stores_delete_scope_request( + name=name, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -6072,105 +12289,107 @@ def get_session_files( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.SessionDirectoryListResponse, response.json()) + deserialized = _deserialize(_models.MemoryStoreDeleteScopeResult, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @distributed_trace - def delete_session_file( # pylint: disable=inconsistent-return-statements - self, agent_name: str, agent_session_id: str, *, path: str, recursive: Optional[bool] = 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. + @overload + def create_memory( + self, + name: str, + *, + scope: str, + content: str, + kind: Union[str, _models.MemoryItemKind], + content_type: str = "application/json", + **kwargs: Any + ) -> _models.MemoryItem: + """Create a memory item in a memory store. - :param agent_name: The name of the agent. Required. - :type agent_name: str - :param agent_session_id: The session ID. Required. - :type agent_session_id: str - :keyword path: The file or directory path to delete, relative to the session home directory. + :param name: The name of the memory store. Required. + :type name: str + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. Required. - :paramtype path: str - :keyword recursive: Whether to recursively delete directory contents. Defaults to false. - Default value is None. - :paramtype recursive: bool - :return: None - :rtype: None + :paramtype scope: str + :keyword content: The content of the memory. Required. + :paramtype content: str + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Required. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem :raises ~azure.core.exceptions.HttpResponseError: """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[None] = kwargs.pop("cls", None) - - _request = build_beta_agents_delete_session_file_request( - agent_name=agent_name, - agent_session_id=agent_session_id, - path=path, - recursive=recursive, - 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) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [204]: - 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, None, {}) # type: ignore + @overload + 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. -class BetaEvaluationTaxonomiesOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. + :param name: The name of the memory store. Required. + :type name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`evaluation_taxonomies` attribute. - """ + @overload + 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. - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + :param name: The name of the memory store. Required. + :type name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ @distributed_trace - def get(self, name: str, **kwargs: Any) -> _models.EvaluationTaxonomy: - """Get an evaluation run by name. + def create_memory( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + scope: str = _Unset, + content: str = _Unset, + kind: Union[str, _models.MemoryItemKind] = _Unset, + **kwargs: Any + ) -> _models.MemoryItem: + """Create a memory item in a memory store. - :param name: The name of the resource. Required. + :param name: The name of the memory store. Required. :type name: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :paramtype scope: str + :keyword content: The content of the memory. Required. + :paramtype content: str + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Required. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -6181,14 +12400,33 @@ def get(self, name: str, **kwargs: Any) -> _models.EvaluationTaxonomy: } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryItem] = kwargs.pop("cls", None) - _request = build_beta_evaluation_taxonomies_get_request( + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + if content is _Unset: + raise TypeError("missing required argument: content") + if kind is _Unset: + raise TypeError("missing required argument: kind") + body = {"content": content, "kind": kind, "scope": scope} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_memory_stores_create_memory_request( name=name, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -6212,122 +12450,98 @@ def get(self, name: str, **kwargs: Any) -> _models.EvaluationTaxonomy: except (StreamConsumedError, StreamClosedError): pass map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) + deserialized = _deserialize(_models.MemoryItem, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @distributed_trace - def list( - self, *, input_name: Optional[str] = None, input_type: Optional[str] = None, **kwargs: Any - ) -> ItemPaged["_models.EvaluationTaxonomy"]: - """List evaluation taxonomies. + @overload + 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. - :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. - :paramtype input_type: str - :return: An iterator like instance of EvaluationTaxonomy - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.EvaluationTaxonomy] + :param name: The name of the memory store. Required. + :type name: str + :param memory_id: The ID of the memory item to update. Required. + :type memory_id: str + :keyword content: The updated content of the memory. Required. + :paramtype content: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[List[_models.EvaluationTaxonomy]] = kwargs.pop("cls", None) - - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_beta_evaluation_taxonomies_list_request( - input_name=input_name, - input_type=input_type, - 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) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", - urllib.parse.urljoin(next_link, _parsed_next_link.path), - params=_next_request_params, - headers=_headers, - ) - 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 - - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.EvaluationTaxonomy], - deserialized.get("value", []), - ) - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) - - def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response + @overload + 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. - if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + :param name: The name of the memory store. Required. + :type name: str + :param memory_id: The ID of the memory item to update. Required. + :type memory_id: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ - return pipeline_response + @overload + 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. - return ItemPaged(get_next, extract_data) + :param name: The name of the memory store. Required. + :type name: str + :param memory_id: The ID of the memory item to update. Required. + :type memory_id: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem + :raises ~azure.core.exceptions.HttpResponseError: + """ @distributed_trace - def delete(self, name: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements - """Delete an evaluation taxonomy by name. + 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. - :param name: The name of the resource. Required. + :param name: The name of the memory store. Required. :type name: str - :return: None - :rtype: None + :param memory_id: The ID of the memory item to update. Required. + :type memory_id: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword content: The updated content of the memory. Required. + :paramtype content: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -6338,14 +12552,30 @@ def delete(self, name: str, **kwargs: Any) -> None: # pylint: disable=inconsist } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[None] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.MemoryItem] = kwargs.pop("cls", None) - _request = build_beta_evaluation_taxonomies_delete_request( + if body is _Unset: + if content is _Unset: + raise TypeError("missing required argument: content") + body = {"content": content} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_memory_stores_update_memory_request( name=name, + memory_id=memory_id, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -6354,87 +12584,47 @@ def delete(self, name: str, **kwargs: Any) -> None: # pylint: disable=inconsist } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _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 ) response = pipeline_response.http_response - if response.status_code not in [204]: + 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) - raise HttpResponseError(response=response) - - if cls: - return cls(pipeline_response, None, {}) # type: ignore - - @overload - def create( - self, name: str, body: _models.EvaluationTaxonomy, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Create an evaluation taxonomy. - - :param name: The name of the evaluation taxonomy. Required. - :type name: str - :param body: The evaluation taxonomy. Required. - :type body: ~azure.ai.projects.models.EvaluationTaxonomy - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - def create( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Create an evaluation taxonomy. - - :param name: The name of the evaluation taxonomy. Required. - :type name: str - :param body: The evaluation taxonomy. Required. - :type body: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy - :raises ~azure.core.exceptions.HttpResponseError: - """ + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) - @overload - def create( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Create an evaluation taxonomy. + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.MemoryItem, response.json()) - :param name: The name of the evaluation taxonomy. Required. - :type name: str - :param body: The evaluation taxonomy. Required. - :type body: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy - :raises ~azure.core.exceptions.HttpResponseError: - """ + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore @distributed_trace - def create( - self, name: str, body: Union[_models.EvaluationTaxonomy, JSON, IO[bytes]], **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Create an evaluation taxonomy. + def get_memory(self, name: str, memory_id: str, **kwargs: Any) -> _models.MemoryItem: + """Retrieve a memory item from a memory store. - :param name: The name of the evaluation taxonomy. Required. + :param name: The name of the memory store. Required. :type name: str - :param body: The evaluation taxonomy. Is one of the following types: EvaluationTaxonomy, JSON, - IO[bytes] Required. - :type body: ~azure.ai.projects.models.EvaluationTaxonomy or JSON or IO[bytes] - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :param memory_id: The ID of the memory item to retrieve. Required. + :type memory_id: str + :return: MemoryItem. The MemoryItem is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.MemoryItem :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -6445,24 +12635,15 @@ def create( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[_models.MemoryItem] = kwargs.pop("cls", None) - _request = build_beta_evaluation_taxonomies_create_request( + _request = build_beta_memory_stores_get_memory_request( name=name, - content_type=content_type, + memory_id=memory_id, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -6479,19 +12660,23 @@ def create( response = pipeline_response.http_response - if response.status_code not in [200, 201]: + 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) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) + deserialized = _deserialize(_models.MemoryItem, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -6499,74 +12684,187 @@ def create( return deserialized # type: ignore @overload - def update( - self, name: str, body: _models.EvaluationTaxonomy, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Update an evaluation taxonomy. + def list_memories( + self, + name: str, + *, + scope: str, + kind: Optional[Union[str, _models.MemoryItemKind]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> ItemPaged["_models.MemoryItem"]: + """List all memory items in a memory store. - :param name: The name of the evaluation taxonomy. Required. + :param name: The name of the memory store. Required. :type name: str - :param body: The evaluation taxonomy. Required. - :type body: ~azure.ai.projects.models.EvaluationTaxonomy + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :paramtype scope: str + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Default value is None. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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 :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :return: An iterator like instance of MemoryItem + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.MemoryItem] :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def update( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Update an evaluation taxonomy. + def list_memories( + self, + name: str, + body: JSON, + *, + kind: Optional[Union[str, _models.MemoryItemKind]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> ItemPaged["_models.MemoryItem"]: + """List all memory items in a memory store. - :param name: The name of the evaluation taxonomy. Required. + :param name: The name of the memory store. Required. :type name: str - :param body: The evaluation taxonomy. Required. + :param body: Required. :type body: JSON + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Default value is None. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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 :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :return: An iterator like instance of MemoryItem + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.MemoryItem] :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def update( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Update an evaluation taxonomy. + def list_memories( + self, + name: str, + body: IO[bytes], + *, + kind: Optional[Union[str, _models.MemoryItemKind]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> ItemPaged["_models.MemoryItem"]: + """List all memory items in a memory store. - :param name: The name of the evaluation taxonomy. Required. + :param name: The name of the memory store. Required. :type name: str - :param body: The evaluation taxonomy. Required. + :param body: Required. :type body: IO[bytes] + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Default value is None. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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 :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :return: An iterator like instance of MemoryItem + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.MemoryItem] :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def update( - self, name: str, body: Union[_models.EvaluationTaxonomy, JSON, IO[bytes]], **kwargs: Any - ) -> _models.EvaluationTaxonomy: - """Update an evaluation taxonomy. + def list_memories( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + scope: str = _Unset, + kind: Optional[Union[str, _models.MemoryItemKind]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_models.MemoryItem"]: + """List all memory items in a memory store. - :param name: The name of the evaluation taxonomy. Required. + :param name: The name of the memory store. Required. :type name: str - :param body: The evaluation taxonomy. Is one of the following types: EvaluationTaxonomy, JSON, - IO[bytes] Required. - :type body: ~azure.ai.projects.models.EvaluationTaxonomy or JSON or IO[bytes] - :return: EvaluationTaxonomy. The EvaluationTaxonomy is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluationTaxonomy + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. + Required. + :paramtype scope: str + :keyword kind: The kind of the memory item. Known values are: "user_profile", "chat_summary", + and "procedural". Default value is None. + :paramtype kind: str or ~azure.ai.projects.models.MemoryItemKind + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of MemoryItem + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.MemoryItem] :raises ~azure.core.exceptions.HttpResponseError: """ + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[List[_models.MemoryItem]] = kwargs.pop("cls", None) + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -6574,13 +12872,6 @@ def update( 304: ResourceNotModifiedError, } error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.EvaluationTaxonomy] = kwargs.pop("cls", None) - content_type = content_type or "application/json" _content = None if isinstance(body, (IOBase, bytes)): @@ -6588,11 +12879,92 @@ def update( else: _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_evaluation_taxonomies_update_request( + def prepare_request(_continuation_token=None): + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = {"scope": scope} + body = {k: v for k, v in body.items() if v is not None} + + _request = build_beta_memory_stores_list_memories_request( + name=name, + kind=kind, + limit=limit, + order=order, + after=_continuation_token, + before=before, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.MemoryItem], + 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) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def delete_memory(self, name: str, memory_id: str, **kwargs: Any) -> _models.DeleteMemoryResult: + """Delete a memory item from a memory store. + + :param name: The name of the memory store. Required. + :type name: str + :param memory_id: The ID of the memory item to delete. Required. + :type memory_id: str + :return: DeleteMemoryResult. The DeleteMemoryResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DeleteMemoryResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.DeleteMemoryResult] = kwargs.pop("cls", None) + + _request = build_beta_memory_stores_delete_memory_request( name=name, - content_type=content_type, + memory_id=memory_id, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -6616,12 +12988,16 @@ def update( except (StreamConsumedError, StreamClosedError): pass map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluationTaxonomy, response.json()) + deserialized = _deserialize(_models.DeleteMemoryResult, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -6629,14 +13005,14 @@ def update( return deserialized # type: ignore -class BetaEvaluatorsOperations: +class BetaModelsOperations: """ .. warning:: **DO NOT** instantiate this class directly. Instead, you should access the following operations through :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`evaluators` attribute. + :attr:`models` attribute. """ def __init__(self, *args, **kwargs) -> None: @@ -6647,33 +13023,19 @@ def __init__(self, *args, **kwargs) -> None: self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") @distributed_trace - def list_versions( - self, - name: str, - *, - type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, - limit: Optional[int] = None, - **kwargs: Any - ) -> ItemPaged["_models.EvaluatorVersion"]: - """List all versions of the given evaluator. + def list_versions(self, name: str, **kwargs: Any) -> ItemPaged["_models.ModelVersion"]: + """List all versions of the given ModelVersion. :param name: The name of the resource. Required. :type name: str - :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 - value is None. - :paramtype type: str or str or str or str - :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the default is 20. Default value is None. - :paramtype limit: int - :return: An iterator like instance of EvaluatorVersion - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.EvaluatorVersion] + :return: An iterator like instance of ModelVersion + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.ModelVersion] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.EvaluatorVersion]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.ModelVersion]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -6686,10 +13048,8 @@ def list_versions( def prepare_request(next_link=None): if not next_link: - _request = build_beta_evaluators_list_versions_request( + _request = build_beta_models_list_versions_request( name=name, - type=type, - limit=limit, api_version=self._config.api_version, headers=_headers, params=_params, @@ -6729,7 +13089,7 @@ def prepare_request(next_link=None): def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.EvaluatorVersion], + List[_models.ModelVersion], deserialized.get("value", []), ) if cls: @@ -6754,30 +13114,17 @@ def get_next(next_link=None): return ItemPaged(get_next, extract_data) @distributed_trace - def list( - self, - *, - type: Optional[Union[Literal["builtin"], Literal["custom"], Literal["all"], str]] = None, - limit: Optional[int] = None, - **kwargs: Any - ) -> ItemPaged["_models.EvaluatorVersion"]: - """List the latest version of each evaluator. + def list(self, **kwargs: Any) -> ItemPaged["_models.ModelVersion"]: + """List the latest version of each ModelVersion. - :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 - value is None. - :paramtype type: str or str or str or str - :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the default is 20. Default value is None. - :paramtype limit: int - :return: An iterator like instance of EvaluatorVersion - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.EvaluatorVersion] + :return: An iterator like instance of ModelVersion + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.ModelVersion] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.EvaluatorVersion]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.ModelVersion]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -6790,9 +13137,7 @@ def list( def prepare_request(next_link=None): if not next_link: - _request = build_beta_evaluators_list_request( - type=type, - limit=limit, + _request = build_beta_models_list_request( api_version=self._config.api_version, headers=_headers, params=_params, @@ -6832,7 +13177,7 @@ def prepare_request(next_link=None): def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.EvaluatorVersion], + List[_models.ModelVersion], deserialized.get("value", []), ) if cls: @@ -6857,16 +13202,16 @@ def get_next(next_link=None): return ItemPaged(get_next, extract_data) @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. + 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. :param name: The name of the resource. Required. :type name: str - :param version: The specific version id of the EvaluatorVersion to retrieve. Required. + :param version: The specific version id of the ModelVersion to retrieve. Required. :type version: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :return: ModelVersion. The ModelVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ModelVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -6880,9 +13225,9 @@ def get_version(self, name: str, version: str, **kwargs: Any) -> _models.Evaluat _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) + cls: ClsType[_models.ModelVersion] = kwargs.pop("cls", None) - _request = build_beta_evaluators_get_version_request( + _request = build_beta_models_get_request( name=name, version=version, api_version=self._config.api_version, @@ -6914,7 +13259,7 @@ def get_version(self, name: str, version: str, **kwargs: Any) -> _models.Evaluat if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluatorVersion, response.json()) + deserialized = _deserialize(_models.ModelVersion, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -6922,15 +13267,13 @@ def get_version(self, name: str, version: str, **kwargs: Any) -> _models.Evaluat return deserialized # type: ignore @distributed_trace - 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. + 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 + was deleted successfully or if the ModelVersion does not exist. :param name: The name of the resource. Required. :type name: str - :param version: The version of the EvaluatorVersion to delete. Required. + :param version: The version of the ModelVersion to delete. Required. :type version: str :return: None :rtype: None @@ -6949,7 +13292,7 @@ def delete_version( # pylint: disable=inconsistent-return-statements cls: ClsType[None] = kwargs.pop("cls", None) - _request = build_beta_evaluators_delete_version_request( + _request = build_beta_models_delete_request( name=name, version=version, api_version=self._config.api_version, @@ -6968,7 +13311,7 @@ def delete_version( # pylint: disable=inconsistent-return-statements response = pipeline_response.http_response - if response.status_code not in [204]: + if response.status_code not in [200]: map_error(status_code=response.status_code, response=response, error_map=error_map) raise HttpResponseError(response=response) @@ -6976,77 +13319,96 @@ def delete_version( # pylint: disable=inconsistent-return-statements return cls(pipeline_response, None, {}) # type: ignore @overload - def create_version( + def update( self, name: str, - evaluator_version: _models.EvaluatorVersion, + version: str, + body: _models.UpdateModelVersionRequest, *, - content_type: str = "application/json", + content_type: str = "application/merge-patch+json", **kwargs: Any - ) -> _models.EvaluatorVersion: - """Create a new EvaluatorVersion with auto incremented version id. + ) -> _models.ModelVersion: + """Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str - :param evaluator_version: Required. - :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion + :param version: The specific version id of the UpdateModelVersionRequest to create or update. + Required. + :type version: str + :param body: The UpdateModelVersionRequest to create or update. Required. + :type body: ~azure.ai.projects.models.UpdateModelVersionRequest :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". + Default value is "application/merge-patch+json". :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :return: ModelVersion. The ModelVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ModelVersion :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + def update( + self, name: str, version: str, body: JSON, *, content_type: str = "application/merge-patch+json", **kwargs: Any + ) -> _models.ModelVersion: + """Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str - :param evaluator_version: Required. - :type evaluator_version: JSON + :param version: The specific version id of the UpdateModelVersionRequest to create or update. + Required. + :type version: str + :param body: The UpdateModelVersionRequest to create or update. Required. + :type body: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". + Default value is "application/merge-patch+json". :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :return: ModelVersion. The ModelVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ModelVersion :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + def update( + self, + name: str, + version: str, + body: IO[bytes], + *, + content_type: str = "application/merge-patch+json", + **kwargs: Any + ) -> _models.ModelVersion: + """Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str - :param evaluator_version: Required. - :type evaluator_version: IO[bytes] + :param version: The specific version id of the UpdateModelVersionRequest to create or update. + Required. + :type version: str + :param body: The UpdateModelVersionRequest to create or update. Required. + :type body: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". + Default value is "application/merge-patch+json". :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :return: ModelVersion. The ModelVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ModelVersion :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - 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. + def update( + self, name: str, version: str, body: Union[_models.UpdateModelVersionRequest, JSON, IO[bytes]], **kwargs: Any + ) -> _models.ModelVersion: + """Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str - :param evaluator_version: Is one of the following types: EvaluatorVersion, JSON, IO[bytes] + :param version: The specific version id of the UpdateModelVersionRequest to create or update. Required. - :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion or JSON or IO[bytes] - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :type version: str + :param body: The UpdateModelVersionRequest to create or update. Is one of the following types: + UpdateModelVersionRequest, JSON, IO[bytes] Required. + :type body: ~azure.ai.projects.models.UpdateModelVersionRequest or JSON or IO[bytes] + :return: ModelVersion. The ModelVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ModelVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7061,17 +13423,173 @@ def create_version( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) + cls: ClsType[_models.ModelVersion] = kwargs.pop("cls", None) + + content_type = content_type or "application/merge-patch+json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_models_update_request( + name=name, + version=version, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 201]: + 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) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.ModelVersion, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + def pending_create_version( + self, + name: str, + version: str, + model_version: _models.ModelVersion, + *, + 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. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param model_version: Model version to create. Required. + :type model_version: ~azure.ai.projects.models.ModelVersion + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: CreateAsyncResponse. The CreateAsyncResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CreateAsyncResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + 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. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param model_version: Model version to create. Required. + :type model_version: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: CreateAsyncResponse. The CreateAsyncResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CreateAsyncResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def pending_create_version( + self, + name: str, + version: str, + model_version: IO[bytes], + *, + 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. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param model_version: Model version to create. Required. + :type model_version: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: CreateAsyncResponse. The CreateAsyncResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CreateAsyncResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + 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. + + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param model_version: Model version to create. Is one of the following types: ModelVersion, + JSON, IO[bytes] Required. + :type model_version: ~azure.ai.projects.models.ModelVersion or JSON or IO[bytes] + :return: CreateAsyncResponse. The CreateAsyncResponse is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.CreateAsyncResponse + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.CreateAsyncResponse] = kwargs.pop("cls", None) content_type = content_type or "application/json" _content = None - if isinstance(evaluator_version, (IOBase, bytes)): - _content = evaluator_version + if isinstance(model_version, (IOBase, bytes)): + _content = model_version else: - _content = json.dumps(evaluator_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(model_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_evaluators_create_version_request( + _request = build_beta_models_pending_create_version_request( name=name, + version=version, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -7091,7 +13609,7 @@ def create_version( response = pipeline_response.http_response - if response.status_code not in [201]: + if response.status_code not in [202]: if _stream: try: response.read() # Load the body in memory and close the socket @@ -7100,107 +13618,121 @@ def create_version( map_error(status_code=response.status_code, response=response, error_map=error_map) raise HttpResponseError(response=response) + response_headers = {} + response_headers["Location"] = self._deserialize("str", response.headers.get("Location")) + if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluatorVersion, response.json()) + deserialized = _deserialize(_models.CreateAsyncResponse, response.json()) if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, deserialized, response_headers) # type: ignore return deserialized # type: ignore @overload - def update_version( + def pending_upload( self, name: str, version: str, - evaluator_version: _models.EvaluatorVersion, + pending_upload_request: _models.ModelPendingUploadRequest, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + ) -> _models.ModelPendingUploadResponse: + """Start or retrieve a pending upload for a model version. - :param name: The name of the resource. Required. + :param name: Name of the model. Required. :type name: str - :param version: The version of the EvaluatorVersion to update. Required. + :param version: Version of the model. Required. :type version: str - :param evaluator_version: Evaluator resource. Required. - :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion + :param pending_upload_request: Required. + :type pending_upload_request: ~azure.ai.projects.models.ModelPendingUploadRequest :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :return: ModelPendingUploadResponse. The ModelPendingUploadResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.ModelPendingUploadResponse :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + def pending_upload( + self, + name: str, + version: str, + pending_upload_request: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.ModelPendingUploadResponse: + """Start or retrieve a pending upload for a model version. - :param name: The name of the resource. Required. + :param name: Name of the model. Required. :type name: str - :param version: The version of the EvaluatorVersion to update. Required. + :param version: Version of the model. Required. :type version: str - :param evaluator_version: Evaluator resource. Required. - :type evaluator_version: JSON + :param pending_upload_request: Required. + :type pending_upload_request: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :return: ModelPendingUploadResponse. The ModelPendingUploadResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.ModelPendingUploadResponse :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def update_version( + def pending_upload( self, name: str, version: str, - evaluator_version: IO[bytes], + pending_upload_request: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + ) -> _models.ModelPendingUploadResponse: + """Start or retrieve a pending upload for a model version. - :param name: The name of the resource. Required. + :param name: Name of the model. Required. :type name: str - :param version: The version of the EvaluatorVersion to update. Required. + :param version: Version of the model. Required. :type version: str - :param evaluator_version: Evaluator resource. Required. - :type evaluator_version: IO[bytes] + :param pending_upload_request: Required. + :type pending_upload_request: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :return: ModelPendingUploadResponse. The ModelPendingUploadResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.ModelPendingUploadResponse :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def update_version( + def pending_upload( self, name: str, version: str, - evaluator_version: Union[_models.EvaluatorVersion, JSON, IO[bytes]], + pending_upload_request: Union[_models.ModelPendingUploadRequest, JSON, IO[bytes]], **kwargs: Any - ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + ) -> _models.ModelPendingUploadResponse: + """Start or retrieve a pending upload for a model version. - :param name: The name of the resource. Required. + :param name: Name of the model. Required. :type name: str - :param version: The version of the EvaluatorVersion to update. Required. + :param version: Version of the model. Required. :type version: str - :param evaluator_version: Evaluator resource. Is one of the following types: EvaluatorVersion, - JSON, IO[bytes] Required. - :type evaluator_version: ~azure.ai.projects.models.EvaluatorVersion or JSON or IO[bytes] - :return: EvaluatorVersion. The EvaluatorVersion is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.EvaluatorVersion + :param pending_upload_request: Is one of the following types: ModelPendingUploadRequest, JSON, + IO[bytes] Required. + :type pending_upload_request: ~azure.ai.projects.models.ModelPendingUploadRequest or JSON or + IO[bytes] + :return: ModelPendingUploadResponse. The ModelPendingUploadResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.ModelPendingUploadResponse :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7215,16 +13747,16 @@ def update_version( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.EvaluatorVersion] = kwargs.pop("cls", None) + cls: ClsType[_models.ModelPendingUploadResponse] = kwargs.pop("cls", None) content_type = content_type or "application/json" _content = None - if isinstance(evaluator_version, (IOBase, bytes)): - _content = evaluator_version + if isinstance(pending_upload_request, (IOBase, bytes)): + _content = pending_upload_request else: - _content = json.dumps(evaluator_version, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(pending_upload_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_evaluators_update_version_request( + _request = build_beta_models_pending_upload_request( name=name, version=version, content_type=content_type, @@ -7258,87 +13790,110 @@ def update_version( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.EvaluatorVersion, response.json()) + deserialized = _deserialize(_models.ModelPendingUploadResponse, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - -class BetaInsightsOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`insights` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - @overload - def generate( - self, insight: _models.Insight, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.Insight: - """Generate Insights. + def get_credentials( + self, + name: str, + version: str, + credential_request: _models.ModelCredentialRequest, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.DatasetCredential: + """Get credentials for a model version asset. - :param insight: Complete evaluation configuration including data source, evaluators, and result - settings. Required. - :type insight: ~azure.ai.projects.models.Insight + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param credential_request: Required. + :type credential_request: ~azure.ai.projects.models.ModelCredentialRequest :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: Insight. The Insight is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Insight + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def generate(self, insight: JSON, *, content_type: str = "application/json", **kwargs: Any) -> _models.Insight: - """Generate Insights. + def get_credentials( + self, + name: str, + version: str, + credential_request: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.DatasetCredential: + """Get credentials for a model version asset. - :param insight: Complete evaluation configuration including data source, evaluators, and result - settings. Required. - :type insight: JSON + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param credential_request: Required. + :type credential_request: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: Insight. The Insight is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Insight + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def generate(self, insight: IO[bytes], *, content_type: str = "application/json", **kwargs: Any) -> _models.Insight: - """Generate Insights. + def get_credentials( + self, + name: str, + version: str, + credential_request: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.DatasetCredential: + """Get credentials for a model version asset. - :param insight: Complete evaluation configuration including data source, evaluators, and result - settings. Required. - :type insight: IO[bytes] + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param credential_request: Required. + :type credential_request: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: Insight. The Insight is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Insight + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwargs: Any) -> _models.Insight: - """Generate Insights. + def get_credentials( + self, + name: str, + version: str, + credential_request: Union[_models.ModelCredentialRequest, JSON, IO[bytes]], + **kwargs: Any + ) -> _models.DatasetCredential: + """Get credentials for a model version asset. - :param insight: Complete evaluation configuration including data source, evaluators, and result - settings. Is one of the following types: Insight, JSON, IO[bytes] Required. - :type insight: ~azure.ai.projects.models.Insight or JSON or IO[bytes] - :return: Insight. The Insight is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Insight + :param name: Name of the model. Required. + :type name: str + :param version: Version of the model. Required. + :type version: str + :param credential_request: Is one of the following types: ModelCredentialRequest, JSON, + IO[bytes] Required. + :type credential_request: ~azure.ai.projects.models.ModelCredentialRequest or JSON or IO[bytes] + :return: DatasetCredential. The DatasetCredential is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DatasetCredential :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7353,16 +13908,18 @@ def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwargs: A _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.Insight] = kwargs.pop("cls", None) + cls: ClsType[_models.DatasetCredential] = kwargs.pop("cls", None) content_type = content_type or "application/json" _content = None - if isinstance(insight, (IOBase, bytes)): - _content = insight + if isinstance(credential_request, (IOBase, bytes)): + _content = credential_request else: - _content = json.dumps(insight, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(credential_request, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_insights_generate_request( + _request = build_beta_models_get_credentials_request( + name=name, + version=version, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -7382,40 +13939,51 @@ def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwargs: A response = pipeline_response.http_response - if response.status_code not in [201]: + 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, - ) - raise HttpResponseError(response=response, model=error) + raise HttpResponseError(response=response) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.Insight, response.json()) + deserialized = _deserialize(_models.DatasetCredential, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + +class BetaRedTeamsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`red_teams` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + @distributed_trace - def get(self, insight_id: str, *, include_coordinates: Optional[bool] = None, **kwargs: Any) -> _models.Insight: - """Get a specific insight by Id. + def get(self, name: str, **kwargs: Any) -> _models.RedTeam: + """Get a redteam by name. - :param insight_id: The unique identifier for the insights report. Required. - :type insight_id: str - :keyword include_coordinates: Whether to include coordinates for visualization in the response. - Defaults to false. Default value is None. - :paramtype include_coordinates: bool - :return: Insight. The Insight is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Insight + :param name: Identifier of the red team run. Required. + :type name: str + :return: RedTeam. The RedTeam is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.RedTeam :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7429,11 +13997,10 @@ def get(self, insight_id: str, *, include_coordinates: Optional[bool] = None, ** _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.Insight] = kwargs.pop("cls", None) + cls: ClsType[_models.RedTeam] = kwargs.pop("cls", None) - _request = build_beta_insights_get_request( - insight_id=insight_id, - include_coordinates=include_coordinates, + _request = build_beta_red_teams_get_request( + name=name, api_version=self._config.api_version, headers=_headers, params=_params, @@ -7458,55 +14025,30 @@ def get(self, insight_id: str, *, include_coordinates: Optional[bool] = None, ** except (StreamConsumedError, StreamClosedError): pass 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) + raise HttpResponseError(response=response) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.Insight, response.json()) + deserialized = _deserialize(_models.RedTeam, response.json()) if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore - - return deserialized # type: ignore - - @distributed_trace - def list( - self, - *, - type: Optional[Union[str, _models.InsightType]] = None, - eval_id: Optional[str] = None, - run_id: Optional[str] = None, - agent_name: Optional[str] = None, - include_coordinates: Optional[bool] = None, - **kwargs: Any - ) -> ItemPaged["_models.Insight"]: - """List all insights in reverse chronological order (newest first). - - :keyword type: Filter by the type of analysis. Known values are: "EvaluationRunClusterInsight", - "AgentClusterInsight", and "EvaluationComparison". Default value is None. - :paramtype type: str or ~azure.ai.projects.models.InsightType - :keyword eval_id: Filter by the evaluation ID. Default value is None. - :paramtype eval_id: str - :keyword run_id: Filter by the evaluation run ID. Default value is None. - :paramtype run_id: str - :keyword agent_name: Filter by the agent name. Default value is None. - :paramtype agent_name: str - :keyword include_coordinates: Whether to include coordinates for visualization in the response. - Defaults to false. Default value is None. - :paramtype include_coordinates: bool - :return: An iterator like instance of Insight - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Insight] + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list(self, **kwargs: Any) -> ItemPaged["_models.RedTeam"]: + """List a redteam by name. + + :return: An iterator like instance of RedTeam + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.RedTeam] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.Insight]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.RedTeam]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -7519,12 +14061,7 @@ def list( def prepare_request(next_link=None): if not next_link: - _request = build_beta_insights_list_request( - type=type, - eval_id=eval_id, - run_id=run_id, - agent_name=agent_name, - include_coordinates=include_coordinates, + _request = build_beta_red_teams_list_request( api_version=self._config.api_version, headers=_headers, params=_params, @@ -7564,7 +14101,7 @@ def prepare_request(next_link=None): def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.Insight], + List[_models.RedTeam], deserialized.get("value", []), ) if cls: @@ -7582,25 +14119,140 @@ def get_next(next_link=None): 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) + raise HttpResponseError(response=response) return pipeline_response return ItemPaged(get_next, extract_data) + @overload + def create( + self, red_team: _models.RedTeam, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.RedTeam: + """Creates a redteam run. + + :param red_team: Redteam to be run. Required. + :type red_team: ~azure.ai.projects.models.RedTeam + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: RedTeam. The RedTeam is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.RedTeam + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create(self, red_team: JSON, *, content_type: str = "application/json", **kwargs: Any) -> _models.RedTeam: + """Creates a redteam run. -class BetaMemoryStoresOperations: + :param red_team: Redteam to be run. Required. + :type red_team: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: RedTeam. The RedTeam is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.RedTeam + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create(self, red_team: IO[bytes], *, content_type: str = "application/json", **kwargs: Any) -> _models.RedTeam: + """Creates a redteam run. + + :param red_team: Redteam to be run. Required. + :type red_team: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: RedTeam. The RedTeam is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.RedTeam + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwargs: Any) -> _models.RedTeam: + """Creates a redteam run. + + :param red_team: Redteam to be run. Is one of the following types: RedTeam, JSON, IO[bytes] + Required. + :type red_team: ~azure.ai.projects.models.RedTeam or JSON or IO[bytes] + :return: RedTeam. The RedTeam is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.RedTeam + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.RedTeam] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(red_team, (IOBase, bytes)): + _content = red_team + else: + _content = json.dumps(red_team, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_red_teams_create_request( + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + if response.status_code not in [201]: + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.RedTeam, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + +class BetaRoutinesOperations: """ .. warning:: **DO NOT** instantiate this class directly. Instead, you should access the following operations through :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`memory_stores` attribute. + :attr:`routines` attribute. """ def __init__(self, *args, **kwargs) -> None: @@ -7611,93 +14263,103 @@ def __init__(self, *args, **kwargs) -> None: self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") @overload - def create( + def create_or_update( self, + routine_name: str, *, - name: str, - definition: _models.MemoryStoreDefinition, content_type: str = "application/json", description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, + enabled: Optional[bool] = None, + triggers: Optional[dict[str, _models.RoutineTrigger]] = None, + action: Optional[_models.RoutineAction] = None, **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Create a memory store. + ) -> _models.Routine: + """Create or update a routine. - :keyword name: The name of the memory store. Required. - :paramtype name: str - :keyword definition: The memory store definition. Required. - :paramtype definition: ~azure.ai.projects.models.MemoryStoreDefinition + :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. Default value is "application/json". :paramtype content_type: str - :keyword description: A human-readable description of the memory store. Default value is None. + :keyword description: A human-readable description of the routine. Default value is None. :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default - value is None. - :paramtype metadata: dict[str, str] - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :keyword enabled: Whether the routine is enabled. Default value is None. + :paramtype enabled: bool + :keyword triggers: The triggers configured for the routine. In v1, exactly one trigger entry is + supported. Default value is None. + :paramtype triggers: dict[str, ~azure.ai.projects.models.RoutineTrigger] + :keyword action: The action executed when the routine fires. Default value is None. + :paramtype action: ~azure.ai.projects.models.RoutineAction + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def create( - self, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Create a memory store. + def create_or_update( + self, routine_name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.Routine: + """Create or update a routine. + :param routine_name: The unique name of the routine. Required. + :type routine_name: str :param body: Required. :type body: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def create( - self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Create a memory store. + def create_or_update( + self, routine_name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.Routine: + """Create or update a routine. + :param routine_name: The unique name of the routine. Required. + :type routine_name: str :param body: Required. :type body: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def create( + def create_or_update( self, + routine_name: str, body: Union[JSON, IO[bytes]] = _Unset, *, - name: str = _Unset, - definition: _models.MemoryStoreDefinition = _Unset, description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, + enabled: Optional[bool] = None, + triggers: Optional[dict[str, _models.RoutineTrigger]] = None, + action: Optional[_models.RoutineAction] = None, **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Create a memory store. + ) -> _models.Routine: + """Create or update a 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. :type body: JSON or IO[bytes] - :keyword name: The name of the memory store. Required. - :paramtype name: str - :keyword definition: The memory store definition. Required. - :paramtype definition: ~azure.ai.projects.models.MemoryStoreDefinition - :keyword description: A human-readable description of the memory store. Default value is None. + :keyword description: A human-readable description of the routine. Default value is None. :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default - value is None. - :paramtype metadata: dict[str, str] - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :keyword enabled: Whether the routine is enabled. Default value is None. + :paramtype enabled: bool + :keyword triggers: The triggers configured for the routine. In v1, exactly one trigger entry is + supported. Default value is None. + :paramtype triggers: dict[str, ~azure.ai.projects.models.RoutineTrigger] + :keyword action: The action executed when the routine fires. Default value is None. + :paramtype action: ~azure.ai.projects.models.RoutineAction + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7712,14 +14374,10 @@ def create( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) + cls: ClsType[_models.Routine] = kwargs.pop("cls", None) if body is _Unset: - if name is _Unset: - raise TypeError("missing required argument: name") - if definition is _Unset: - raise TypeError("missing required argument: definition") - body = {"definition": definition, "description": description, "metadata": metadata, "name": name} + body = {"action": action, "description": description, "enabled": enabled, "triggers": triggers} body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None @@ -7728,10 +14386,76 @@ def create( else: _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_memory_stores_create_request( - content_type=content_type, + _request = build_beta_routines_create_or_update_request( + routine_name=routine_name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) + + _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 + ) + + response = pipeline_response.http_response + + 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, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Routine, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def get(self, routine_name: str, **kwargs: Any) -> _models.Routine: + """Retrieve a routine. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.Routine] = kwargs.pop("cls", None) + + _request = build_beta_routines_get_request( + routine_name=routine_name, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -7764,99 +14488,21 @@ def create( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) + deserialized = _deserialize(_models.Routine, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @overload - def update( - self, - name: str, - *, - content_type: str = "application/json", - description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Update a memory store. - - :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. - Default value is "application/json". - :paramtype content_type: str - :keyword description: A human-readable description of the memory store. Default value is None. - :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default - value is None. - :paramtype metadata: dict[str, str] - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - def update( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Update a memory store. - - :param name: The name of the memory store to update. Required. - :type name: str - :param body: Required. - :type body: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - def update( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Update a memory store. - - :param name: The name of the memory store to update. Required. - :type name: str - :param body: Required. - :type body: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails - :raises ~azure.core.exceptions.HttpResponseError: - """ - @distributed_trace - def update( - self, - name: str, - body: Union[JSON, IO[bytes]] = _Unset, - *, - description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - **kwargs: Any - ) -> _models.MemoryStoreDetails: - """Update a memory store. + def enable(self, routine_name: str, **kwargs: Any) -> _models.Routine: + """Enable a routine. - :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. - :type body: JSON or IO[bytes] - :keyword description: A human-readable description of the memory store. Default value is None. - :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the memory store. Default - value is None. - :paramtype metadata: dict[str, str] - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7867,27 +14513,14 @@ def update( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) - - if body is _Unset: - body = {"description": description, "metadata": metadata} - body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[_models.Routine] = kwargs.pop("cls", None) - _request = build_beta_memory_stores_update_request( - name=name, - content_type=content_type, + _request = build_beta_routines_enable_request( + routine_name=routine_name, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -7920,7 +14553,7 @@ def update( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) + deserialized = _deserialize(_models.Routine, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -7928,13 +14561,13 @@ def update( return deserialized # type: ignore @distributed_trace - def get(self, name: str, **kwargs: Any) -> _models.MemoryStoreDetails: - """Retrieve a memory store. + def disable(self, routine_name: str, **kwargs: Any) -> _models.Routine: + """Disable a routine. - :param name: The name of the memory store to retrieve. Required. - :type name: str - :return: MemoryStoreDetails. The MemoryStoreDetails is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDetails + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :return: Routine. The Routine is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Routine :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -7948,10 +14581,10 @@ def get(self, name: str, **kwargs: Any) -> _models.MemoryStoreDetails: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.MemoryStoreDetails] = kwargs.pop("cls", None) + cls: ClsType[_models.Routine] = kwargs.pop("cls", None) - _request = build_beta_memory_stores_get_request( - name=name, + _request = build_beta_routines_disable_request( + routine_name=routine_name, api_version=self._config.api_version, headers=_headers, params=_params, @@ -7985,7 +14618,7 @@ def get(self, name: str, **kwargs: Any) -> _models.MemoryStoreDetails: if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.MemoryStoreDetails, response.json()) + deserialized = _deserialize(_models.Routine, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -7994,37 +14627,169 @@ def get(self, name: str, **kwargs: Any) -> _models.MemoryStoreDetails: @distributed_trace def list( + self, *, limit: Optional[int] = None, before: Optional[str] = None, order: Optional[str] = None, **kwargs: Any + ) -> ItemPaged["_models.Routine"]: + """List routines. + + :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 + None. + :paramtype before: str + :keyword order: The ordering direction. Supported values are asc and desc. Default value is + None. + :paramtype order: str + :return: An iterator like instance of Routine + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Routine] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.Routine]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_beta_routines_list_request( + limit=limit, + after=_continuation_token, + before=before, + order=order, + 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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.Routine], + 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) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def delete(self, routine_name: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements + """Delete a routine. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_routines_delete_request( + routine_name=routine_name, + 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) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + 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, None, {}) # type: ignore + + @distributed_trace + def list_runs( self, + routine_name: str, *, + filter: Optional[str] = None, limit: Optional[int] = None, - order: Optional[Union[str, _models.PageOrder]] = None, before: Optional[str] = None, + order: Optional[str] = None, **kwargs: Any - ) -> ItemPaged["_models.MemoryStoreDetails"]: - """List all memory stores. - - :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the - default is 20. Default value is None. + ) -> ItemPaged["_models.RoutineRun"]: + """List prior runs for a 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 + experiment. Default value is None. + :paramtype filter: str + :keyword limit: The maximum number of runs to return. Default value is None. :paramtype limit: int - :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for - 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 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. + :keyword before: Unsupported. Reserved for future backward pagination support. Default value is + None. :paramtype before: str - :return: An iterator like instance of MemoryStoreDetails - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.MemoryStoreDetails] + :keyword order: The ordering direction. Supported values are asc and desc. Default value is + None. + :paramtype order: str + :return: An iterator like instance of RoutineRun + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.RoutineRun] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.MemoryStoreDetails]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.RoutineRun]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -8036,11 +14801,13 @@ def list( def prepare_request(_continuation_token=None): - _request = build_beta_memory_stores_list_request( + _request = build_beta_routines_list_runs_request( + routine_name=routine_name, + filter=filter, limit=limit, - order=order, after=_continuation_token, before=before, + order=order, api_version=self._config.api_version, headers=_headers, params=_params, @@ -8054,7 +14821,7 @@ def prepare_request(_continuation_token=None): def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.MemoryStoreDetails], + List[_models.RoutineRun], deserialized.get("data", []), ) if cls: @@ -8082,14 +14849,86 @@ def get_next(_continuation_token=None): return ItemPaged(get_next, extract_data) + @overload + def dispatch( + self, + routine_name: str, + *, + content_type: str = "application/json", + payload: Optional[_models.RoutineDispatchPayload] = None, + **kwargs: Any + ) -> _models.DispatchRoutineResult: + """Queue an asynchronous routine dispatch. + + :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. + Default value is "application/json". + :paramtype content_type: str + :keyword payload: A direct action-input override sent downstream when testing a routine. + Default value is None. + :paramtype payload: ~azure.ai.projects.models.RoutineDispatchPayload + :return: DispatchRoutineResult. The DispatchRoutineResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DispatchRoutineResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def dispatch( + self, routine_name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.DispatchRoutineResult: + """Queue an asynchronous routine dispatch. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: DispatchRoutineResult. The DispatchRoutineResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DispatchRoutineResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def dispatch( + self, routine_name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.DispatchRoutineResult: + """Queue an asynchronous routine dispatch. + + :param routine_name: The unique name of the routine. Required. + :type routine_name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: DispatchRoutineResult. The DispatchRoutineResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DispatchRoutineResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + @distributed_trace - def delete(self, name: str, **kwargs: Any) -> _models.DeleteMemoryStoreResult: - """Delete a memory store. + def dispatch( + self, + routine_name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + payload: Optional[_models.RoutineDispatchPayload] = None, + **kwargs: Any + ) -> _models.DispatchRoutineResult: + """Queue an asynchronous routine dispatch. - :param name: The name of the memory store to delete. Required. - :type name: str - :return: DeleteMemoryStoreResult. The DeleteMemoryStoreResult is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.DeleteMemoryStoreResult + :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. + :type body: JSON or IO[bytes] + :keyword payload: A direct action-input override sent downstream when testing a routine. + Default value is None. + :paramtype payload: ~azure.ai.projects.models.RoutineDispatchPayload + :return: DispatchRoutineResult. The DispatchRoutineResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DispatchRoutineResult :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8100,14 +14939,27 @@ def delete(self, name: str, **kwargs: Any) -> _models.DeleteMemoryStoreResult: } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.DeleteMemoryStoreResult] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.DispatchRoutineResult] = kwargs.pop("cls", None) - _request = build_beta_memory_stores_delete_request( - name=name, + if body is _Unset: + body = {"payload": payload} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_beta_routines_dispatch_request( + routine_name=routine_name, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -8140,64 +14992,39 @@ def delete(self, name: str, **kwargs: Any) -> _models.DeleteMemoryStoreResult: if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.DeleteMemoryStoreResult, response.json()) + deserialized = _deserialize(_models.DispatchRoutineResult, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @overload - def _search_memories( - self, - name: str, - *, - scope: str, - content_type: str = "application/json", - items: Optional[List[dict[str, Any]]] = None, - previous_search_id: Optional[str] = None, - options: Optional[_models.MemorySearchOptions] = None, - **kwargs: Any - ) -> _models.MemoryStoreSearchResult: ... - @overload - def _search_memories( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreSearchResult: ... - @overload - def _search_memories( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.MemoryStoreSearchResult: ... + +class BetaSchedulesOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`schedules` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") @distributed_trace - def _search_memories( - self, - name: str, - body: Union[JSON, IO[bytes]] = _Unset, - *, - scope: str = _Unset, - items: Optional[List[dict[str, Any]]] = None, - previous_search_id: Optional[str] = None, - options: Optional[_models.MemorySearchOptions] = None, - **kwargs: Any - ) -> _models.MemoryStoreSearchResult: - """Search for relevant memories from a memory store based on conversation context. + def delete(self, schedule_id: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements + """Delete a schedule. - :param name: The name of the memory store to search. Required. - :type name: str - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. - Required. - :paramtype scope: str - :keyword items: Items for which to search for relevant memories. Default value is None. - :paramtype items: list[dict[str, any]] - :keyword previous_search_id: The unique ID of the previous search request, enabling incremental - memory search from where the last operation left off. Default value is None. - :paramtype previous_search_id: str - :keyword options: Memory search options. Default value is None. - :paramtype options: ~azure.ai.projects.models.MemorySearchOptions - :return: MemoryStoreSearchResult. The MemoryStoreSearchResult is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreSearchResult + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :return: None + :rtype: None :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8208,34 +15035,14 @@ def _search_memories( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.MemoryStoreSearchResult] = kwargs.pop("cls", None) - - if body is _Unset: - if scope is _Unset: - raise TypeError("missing required argument: scope") - body = { - "items": items, - "options": options, - "previous_search_id": previous_search_id, - "scope": scope, - } - body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[None] = kwargs.pop("cls", None) - _request = build_beta_memory_stores_search_memories_request( - name=name, - content_type=content_type, + _request = build_beta_schedules_delete_request( + schedule_id=schedule_id, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -8244,48 +15051,30 @@ def _search_memories( } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = False pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - 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 + if response.status_code not in [204]: 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 _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.MemoryStoreSearchResult, response.json()) + raise HttpResponseError(response=response) if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, None, {}) # type: ignore - return deserialized # type: ignore + @distributed_trace + def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: + """Get a schedule by id. - def _update_memories_initial( - self, - name: str, - body: Union[JSON, IO[bytes]] = _Unset, - *, - scope: str = _Unset, - items: Optional[List[dict[str, Any]]] = None, - previous_update_id: Optional[str] = None, - update_delay: Optional[int] = None, - **kwargs: Any - ) -> Iterator[bytes]: + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :return: Schedule. The Schedule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Schedule + :raises ~azure.core.exceptions.HttpResponseError: + """ error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -8294,34 +15083,14 @@ def _update_memories_initial( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[Iterator[bytes]] = kwargs.pop("cls", None) - - if body is _Unset: - if scope is _Unset: - raise TypeError("missing required argument: scope") - body = { - "items": items, - "previous_update_id": previous_update_id, - "scope": scope, - "update_delay": update_delay, - } - body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[_models.Schedule] = kwargs.pop("cls", None) - _request = build_beta_memory_stores_update_memories_request( - name=name, - content_type=content_type, + _request = build_beta_schedules_get_request( + schedule_id=schedule_id, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -8331,228 +15100,200 @@ def _update_memories_initial( _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = True + _stream = kwargs.pop("stream", False) pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) - response = pipeline_response.http_response - - if response.status_code not in [202]: - 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, - ) - raise HttpResponseError(response=response, model=error) + response = pipeline_response.http_response - response_headers = {} - response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) + 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) + raise HttpResponseError(response=response) - deserialized = response.iter_bytes() if _decompress else response.iter_raw() + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.Schedule, response.json()) if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore + return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - @overload - def _begin_update_memories( - self, - name: str, - *, - scope: str, - content_type: str = "application/json", - items: Optional[List[dict[str, Any]]] = None, - previous_update_id: Optional[str] = None, - update_delay: Optional[int] = None, - **kwargs: Any - ) -> LROPoller[_models.MemoryStoreUpdateCompletedResult]: ... - @overload - def _begin_update_memories( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> LROPoller[_models.MemoryStoreUpdateCompletedResult]: ... - @overload - def _begin_update_memories( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> LROPoller[_models.MemoryStoreUpdateCompletedResult]: ... - @distributed_trace - def _begin_update_memories( + def list( self, - name: str, - body: Union[JSON, IO[bytes]] = _Unset, *, - scope: str = _Unset, - items: Optional[List[dict[str, Any]]] = None, - previous_update_id: Optional[str] = None, - update_delay: Optional[int] = None, + type: Optional[Union[str, _models.ScheduleTaskType]] = None, + enabled: Optional[bool] = None, **kwargs: Any - ) -> LROPoller[_models.MemoryStoreUpdateCompletedResult]: - """Update memory store with conversation memories. + ) -> ItemPaged["_models.Schedule"]: + """List all schedules. - :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. - :type body: JSON or IO[bytes] - :keyword scope: The namespace that logically groups and isolates memories, such as a user ID. - Required. - :paramtype scope: str - :keyword items: Conversation items to be stored in memory. Default value is None. - :paramtype items: list[dict[str, any]] - :keyword previous_update_id: The unique ID of the previous update request, enabling incremental - memory updates from where the last operation left off. Default value is None. - :paramtype previous_update_id: str - :keyword update_delay: Timeout period before processing the memory update in seconds. - If a new update request is received during this period, it will cancel the current request and - reset the timeout. - Set to 0 to immediately trigger the update without delay. - Defaults to 300 (5 minutes). Default value is None. - :paramtype update_delay: int - :return: An instance of LROPoller that returns MemoryStoreUpdateCompletedResult. The - MemoryStoreUpdateCompletedResult is compatible with MutableMapping - :rtype: - ~azure.core.polling.LROPoller[~azure.ai.projects.models.MemoryStoreUpdateCompletedResult] + :keyword type: Filter by the type of schedule. Known values are: "Evaluation" and "Insight". + Default value is None. + :paramtype type: str or ~azure.ai.projects.models.ScheduleTaskType + :keyword enabled: Filter by the enabled status. Default value is None. + :paramtype enabled: bool + :return: An iterator like instance of Schedule + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Schedule] :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.MemoryStoreUpdateCompletedResult] = kwargs.pop("cls", None) - polling: Union[bool, PollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = self._update_memories_initial( - name=name, - body=body, - scope=scope, - items=items, - previous_update_id=previous_update_id, - update_delay=update_delay, - content_type=content_type, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - raw_result.http_response.read() # type: ignore - kwargs.pop("error_map", None) + cls: ClsType[List[_models.Schedule]] = kwargs.pop("cls", None) - def get_long_running_output(pipeline_response): - response_headers = {} - response = pipeline_response.http_response - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) - deserialized = _deserialize(_models.MemoryStoreUpdateCompletedResult, response.json().get("result", {})) - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - return deserialized + def prepare_request(next_link=None): + if not next_link: - path_format_arguments = { - "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } + _request = build_beta_schedules_list_request( + type=type, + enabled=enabled, + 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) - if polling is True: - polling_method: PollingMethod = cast( - PollingMethod, LROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + params=_next_request_params, + headers=_headers, + ) + 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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.Schedule], + deserialized.get("value", []), ) - elif polling is False: - polling_method = cast(PollingMethod, NoPolling()) - else: - polling_method = polling - if cont_token: - return LROPoller[_models.MemoryStoreUpdateCompletedResult].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs ) - return LROPoller[_models.MemoryStoreUpdateCompletedResult]( - self._client, raw_result, get_long_running_output, polling_method # type: ignore - ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) @overload - 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. + 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. - :param name: The name of the memory store. Required. - :type name: str - :keyword scope: The namespace that logically groups and isolates memories to delete, such as a - user ID. Required. - :paramtype scope: str + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :param schedule: The resource instance. Required. + :type schedule: ~azure.ai.projects.models.Schedule :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :return: Schedule. The Schedule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Schedule :raises ~azure.core.exceptions.HttpResponseError: """ @overload - 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. + def create_or_update( + self, schedule_id: str, schedule: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.Schedule: + """Create or update operation template. - :param name: The name of the memory store. Required. - :type name: str - :param body: Required. - :type body: JSON + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :param schedule: The resource instance. Required. + :type schedule: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :return: Schedule. The Schedule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Schedule :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - 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. + """ - :param name: The name of the memory store. Required. - :type name: str - :param body: Required. - :type body: IO[bytes] + @overload + 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. + + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :param schedule: The resource instance. Required. + :type schedule: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :return: Schedule. The Schedule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Schedule :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - 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. + def create_or_update( + self, schedule_id: str, schedule: Union[_models.Schedule, JSON, IO[bytes]], **kwargs: Any + ) -> _models.Schedule: + """Create or update operation template. - :param name: The name of the memory store. Required. - :type name: str - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword scope: The namespace that logically groups and isolates memories to delete, such as a - user ID. Required. - :paramtype scope: str - :return: MemoryStoreDeleteScopeResult. The MemoryStoreDeleteScopeResult is compatible with - MutableMapping - :rtype: ~azure.ai.projects.models.MemoryStoreDeleteScopeResult + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :param schedule: The resource instance. Is one of the following types: Schedule, JSON, + IO[bytes] Required. + :type schedule: ~azure.ai.projects.models.Schedule or JSON or IO[bytes] + :return: Schedule. The Schedule is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.Schedule :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8567,22 +15308,17 @@ def delete_scope( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.MemoryStoreDeleteScopeResult] = kwargs.pop("cls", None) + cls: ClsType[_models.Schedule] = kwargs.pop("cls", None) - if body is _Unset: - if scope is _Unset: - raise TypeError("missing required argument: scope") - body = {"scope": scope} - body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None - if isinstance(body, (IOBase, bytes)): - _content = body + if isinstance(schedule, (IOBase, bytes)): + _content = schedule else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(schedule, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_memory_stores_delete_scope_request( - name=name, + _request = build_beta_schedules_create_or_update_request( + schedule_id=schedule_id, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -8602,55 +15338,35 @@ def delete_scope( response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [200, 201]: 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, - ) - raise HttpResponseError(response=response, model=error) + raise HttpResponseError(response=response) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.MemoryStoreDeleteScopeResult, response.json()) + deserialized = _deserialize(_models.Schedule, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - -class BetaRedTeamsOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`red_teams` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - @distributed_trace - def get(self, name: str, **kwargs: Any) -> _models.RedTeam: - """Get a redteam by name. + def get_run(self, schedule_id: str, run_id: str, **kwargs: Any) -> _models.ScheduleRun: + """Get a schedule run by id. - :param name: Identifier of the red team run. Required. - :type name: str - :return: RedTeam. The RedTeam is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.RedTeam + :param schedule_id: The unique identifier of the schedule. Required. + :type schedule_id: str + :param run_id: The unique identifier of the schedule run. Required. + :type run_id: str + :return: ScheduleRun. The ScheduleRun is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ScheduleRun :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8664,10 +15380,11 @@ def get(self, name: str, **kwargs: Any) -> _models.RedTeam: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.RedTeam] = kwargs.pop("cls", None) + cls: ClsType[_models.ScheduleRun] = kwargs.pop("cls", None) - _request = build_beta_red_teams_get_request( - name=name, + _request = build_beta_schedules_get_run_request( + schedule_id=schedule_id, + run_id=run_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -8692,12 +15409,16 @@ def get(self, name: str, **kwargs: Any) -> _models.RedTeam: except (StreamConsumedError, StreamClosedError): pass map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.RedTeam, response.json()) + deserialized = _deserialize(_models.ScheduleRun, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -8705,17 +15426,31 @@ def get(self, name: str, **kwargs: Any) -> _models.RedTeam: return deserialized # type: ignore @distributed_trace - def list(self, **kwargs: Any) -> ItemPaged["_models.RedTeam"]: - """List a redteam by name. + def list_runs( + self, + schedule_id: str, + *, + type: Optional[Union[str, _models.ScheduleTaskType]] = None, + enabled: Optional[bool] = None, + **kwargs: Any + ) -> ItemPaged["_models.ScheduleRun"]: + """List all schedule runs. - :return: An iterator like instance of RedTeam - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.RedTeam] + :param schedule_id: Identifier of the schedule. Required. + :type schedule_id: str + :keyword type: Filter by the type of schedule. Known values are: "Evaluation" and "Insight". + Default value is None. + :paramtype type: str or ~azure.ai.projects.models.ScheduleTaskType + :keyword enabled: Filter by the enabled status. Default value is None. + :paramtype enabled: bool + :return: An iterator like instance of ScheduleRun + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.ScheduleRun] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.RedTeam]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.ScheduleRun]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -8728,7 +15463,10 @@ def list(self, **kwargs: Any) -> ItemPaged["_models.RedTeam"]: def prepare_request(next_link=None): if not next_link: - _request = build_beta_red_teams_list_request( + _request = build_beta_schedules_list_runs_request( + schedule_id=schedule_id, + type=type, + enabled=enabled, api_version=self._config.api_version, headers=_headers, params=_params, @@ -8768,7 +15506,7 @@ def prepare_request(next_link=None): def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.RedTeam], + List[_models.ScheduleRun], deserialized.get("value", []), ) if cls: @@ -8792,59 +15530,136 @@ def get_next(next_link=None): return ItemPaged(get_next, extract_data) + +class BetaToolboxesOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`toolboxes` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + @overload - def create( - self, red_team: _models.RedTeam, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.RedTeam: - """Creates a redteam run. + def create_version( + self, + name: str, + *, + tools: List[_models.Tool], + content_type: str = "application/json", + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, + skills: Optional[List[_models.ToolboxSkill]] = None, + 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. - :param red_team: Redteam to be run. Required. - :type red_team: ~azure.ai.projects.models.RedTeam + :param name: The name of the toolbox. If the toolbox does not exist, it will be created. + Required. + :type name: str + :keyword tools: The list of tools to include in this version. Required. + :paramtype tools: list[~azure.ai.projects.models.Tool] :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: RedTeam. The RedTeam is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.RedTeam + :keyword description: A human-readable description of the toolbox. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the toolbox. Default value is + None. + :paramtype metadata: dict[str, str] + :keyword skills: The list of skill sources to include in this version. A skill reference + specifies a skill name and optionally a version. If version is omitted, the skill's default + version is used. Default value is None. + :paramtype skills: list[~azure.ai.projects.models.ToolboxSkill] + :keyword policies: Policy configuration for this toolbox version. Default value is None. + :paramtype policies: ~azure.ai.projects.models.ToolboxPolicies + :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxVersionObject :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def create(self, red_team: JSON, *, content_type: str = "application/json", **kwargs: Any) -> _models.RedTeam: - """Creates a redteam run. + 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. - :param red_team: Redteam to be run. Required. - :type red_team: JSON + :param name: The name of the toolbox. If the toolbox does not exist, it will be created. + Required. + :type name: str + :param body: Required. + :type body: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: RedTeam. The RedTeam is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.RedTeam + :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxVersionObject :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def create(self, red_team: IO[bytes], *, content_type: str = "application/json", **kwargs: Any) -> _models.RedTeam: - """Creates a redteam run. + 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. - :param red_team: Redteam to be run. Required. - :type red_team: IO[bytes] + :param name: The name of the toolbox. If the toolbox does not exist, it will be created. + Required. + :type name: str + :param body: Required. + :type body: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: RedTeam. The RedTeam is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.RedTeam + :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxVersionObject :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwargs: Any) -> _models.RedTeam: - """Creates a redteam run. + def create_version( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + tools: List[_models.Tool] = _Unset, + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, + skills: Optional[List[_models.ToolboxSkill]] = None, + 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. - :param red_team: Redteam to be run. Is one of the following types: RedTeam, JSON, IO[bytes] - Required. - :type red_team: ~azure.ai.projects.models.RedTeam or JSON or IO[bytes] - :return: RedTeam. The RedTeam is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.RedTeam + :param name: The name of the toolbox. If the toolbox does not exist, it will be created. + Required. + :type name: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword tools: The list of tools to include in this version. Required. + :paramtype tools: list[~azure.ai.projects.models.Tool] + :keyword description: A human-readable description of the toolbox. Default value is None. + :paramtype description: str + :keyword metadata: Arbitrary key-value metadata to associate with the toolbox. Default value is + None. + :paramtype metadata: dict[str, str] + :keyword skills: The list of skill sources to include in this version. A skill reference + specifies a skill name and optionally a version. If version is omitted, the skill's default + version is used. Default value is None. + :paramtype skills: list[~azure.ai.projects.models.ToolboxSkill] + :keyword policies: Policy configuration for this toolbox version. Default value is None. + :paramtype policies: ~azure.ai.projects.models.ToolboxPolicies + :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxVersionObject :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8859,16 +15674,28 @@ def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwargs: An _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.RedTeam] = kwargs.pop("cls", None) + cls: ClsType[_models.ToolboxVersionObject] = kwargs.pop("cls", None) + if body is _Unset: + if tools is _Unset: + raise TypeError("missing required argument: tools") + body = { + "description": description, + "metadata": metadata, + "policies": policies, + "skills": skills, + "tools": tools, + } + body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None - if isinstance(red_team, (IOBase, bytes)): - _content = red_team + if isinstance(body, (IOBase, bytes)): + _content = body else: - _content = json.dumps(red_team, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_red_teams_create_request( + _request = build_beta_toolboxes_create_version_request( + name=name, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -8888,7 +15715,7 @@ def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwargs: An response = pipeline_response.http_response - if response.status_code not in [201]: + if response.status_code not in [200]: if _stream: try: response.read() # Load the body in memory and close the socket @@ -8904,87 +15731,21 @@ def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwargs: An if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.RedTeam, response.json()) + deserialized = _deserialize(_models.ToolboxVersionObject, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore - -class BetaSchedulesOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`schedules` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - - @distributed_trace - def delete(self, schedule_id: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements - """Delete a schedule. - - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[None] = kwargs.pop("cls", None) - - _request = build_beta_schedules_delete_request( - schedule_id=schedule_id, - 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) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [204]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if cls: - return cls(pipeline_response, None, {}) # type: ignore - @distributed_trace - def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: - """Get a schedule by id. + def get(self, name: str, **kwargs: Any) -> _models.ToolboxObject: + """Retrieve a toolbox. - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :return: Schedule. The Schedule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Schedule + :param name: The name of the toolbox to retrieve. Required. + :type name: str + :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxObject :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -8998,10 +15759,10 @@ def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.Schedule] = kwargs.pop("cls", None) + cls: ClsType[_models.ToolboxObject] = kwargs.pop("cls", None) - _request = build_beta_schedules_get_request( - schedule_id=schedule_id, + _request = build_beta_toolboxes_get_request( + name=name, api_version=self._config.api_version, headers=_headers, params=_params, @@ -9026,12 +15787,16 @@ def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: except (StreamConsumedError, StreamClosedError): pass map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.Schedule, response.json()) + deserialized = _deserialize(_models.ToolboxObject, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -9042,25 +15807,35 @@ def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: def list( self, *, - type: Optional[Union[str, _models.ScheduleTaskType]] = None, - enabled: Optional[bool] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, **kwargs: Any - ) -> ItemPaged["_models.Schedule"]: - """List all schedules. + ) -> ItemPaged["_models.ToolboxObject"]: + """List all toolboxes. - :keyword type: Filter by the type of schedule. Known values are: "Evaluation" and "Insight". + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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 type: str or ~azure.ai.projects.models.ScheduleTaskType - :keyword enabled: Filter by the enabled status. Default value is None. - :paramtype enabled: bool - :return: An iterator like instance of Schedule - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Schedule] + :paramtype before: str + :return: An iterator like instance of ToolboxObject + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.ToolboxObject] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.Schedule]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.ToolboxObject]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -9070,60 +15845,129 @@ def list( } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(next_link=None): - if not next_link: + def prepare_request(_continuation_token=None): - _request = build_beta_schedules_list_request( - type=type, - enabled=enabled, - api_version=self._config.api_version, - headers=_headers, - params=_params, + _request = build_beta_toolboxes_list_request( + 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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.ToolboxObject], + 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) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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, ) - 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) + raise HttpResponseError(response=response, model=error) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def list_versions( + self, + name: str, + *, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_models.ToolboxVersionObject"]: + """List all versions of a toolbox. + + :param name: The name of the toolbox to list versions for. Required. + :type name: str + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of ToolboxVersionObject + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.ToolboxVersionObject] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", - urllib.parse.urljoin(next_link, _parsed_next_link.path), - params=_next_request_params, - headers=_headers, - ) - 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) + cls: ClsType[List[_models.ToolboxVersionObject]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + _request = build_beta_toolboxes_list_versions_request( + name=name, + 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 def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.Schedule], - deserialized.get("value", []), + List[_models.ToolboxVersionObject], + deserialized.get("data", []), ) if cls: list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) + return deserialized.get("last_id") or None, iter(list_of_elem) - def get_next(next_link=None): - _request = prepare_request(next_link) + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) _stream = False pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access @@ -9133,79 +15977,26 @@ def get_next(next_link=None): if response.status_code not in [200]: map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) return pipeline_response return ItemPaged(get_next, extract_data) - @overload - 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. - - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :param schedule: The resource instance. Required. - :type schedule: ~azure.ai.projects.models.Schedule - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: Schedule. The Schedule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Schedule - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - def create_or_update( - self, schedule_id: str, schedule: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.Schedule: - """Create or update operation template. - - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :param schedule: The resource instance. Required. - :type schedule: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: Schedule. The Schedule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Schedule - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - 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. - - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :param schedule: The resource instance. Required. - :type schedule: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: Schedule. The Schedule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Schedule - :raises ~azure.core.exceptions.HttpResponseError: - """ - @distributed_trace - def create_or_update( - self, schedule_id: str, schedule: Union[_models.Schedule, JSON, IO[bytes]], **kwargs: Any - ) -> _models.Schedule: - """Create or update operation template. + def get_version(self, name: str, version: str, **kwargs: Any) -> _models.ToolboxVersionObject: + """Retrieve a specific version of a toolbox. - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :param schedule: The resource instance. Is one of the following types: Schedule, JSON, - IO[bytes] Required. - :type schedule: ~azure.ai.projects.models.Schedule or JSON or IO[bytes] - :return: Schedule. The Schedule is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.Schedule + :param name: The name of the toolbox. Required. + :type name: str + :param version: The version identifier to retrieve. Required. + :type version: str + :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxVersionObject :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -9216,24 +16007,15 @@ def create_or_update( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.Schedule] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _content = None - if isinstance(schedule, (IOBase, bytes)): - _content = schedule - else: - _content = json.dumps(schedule, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[_models.ToolboxVersionObject] = kwargs.pop("cls", None) - _request = build_beta_schedules_create_or_update_request( - schedule_id=schedule_id, - content_type=content_type, + _request = build_beta_toolboxes_get_version_request( + name=name, + version=version, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -9250,35 +16032,99 @@ def create_or_update( response = pipeline_response.http_response - if response.status_code not in [200, 201]: + 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) - raise HttpResponseError(response=response) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.Schedule, response.json()) + deserialized = _deserialize(_models.ToolboxVersionObject, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + @overload + def update( + self, name: str, *, default_version: str, content_type: str = "application/json", **kwargs: Any + ) -> _models.ToolboxObject: + """Update a toolbox to point to a specific 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, + the toolbox's default version will resolve to this version instead of the latest. Required. + :paramtype default_version: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def update( + self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.ToolboxObject: + """Update a toolbox to point to a specific version. + + :param name: The name of the toolbox to update. Required. + :type name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def update( + self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.ToolboxObject: + """Update a toolbox to point to a specific version. + + :param name: The name of the toolbox to update. Required. + :type name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + @distributed_trace - def get_run(self, schedule_id: str, run_id: str, **kwargs: Any) -> _models.ScheduleRun: - """Get a schedule run by id. + def update( + self, name: str, body: Union[JSON, IO[bytes]] = _Unset, *, default_version: str = _Unset, **kwargs: Any + ) -> _models.ToolboxObject: + """Update a toolbox to point to a specific version. - :param schedule_id: The unique identifier of the schedule. Required. - :type schedule_id: str - :param run_id: The unique identifier of the schedule run. Required. - :type run_id: str - :return: ScheduleRun. The ScheduleRun is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ScheduleRun + :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. + :type body: JSON or IO[bytes] + :keyword default_version: The version identifier that the toolbox should point to. When set, + the toolbox's default version will resolve to this version instead of the latest. Required. + :paramtype default_version: str + :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.ToolboxObject :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -9289,15 +16135,29 @@ def get_run(self, schedule_id: str, run_id: str, **kwargs: Any) -> _models.Sched } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.ScheduleRun] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.ToolboxObject] = kwargs.pop("cls", None) + + if body is _Unset: + if default_version is _Unset: + raise TypeError("missing required argument: default_version") + body = {"default_version": default_version} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_schedules_get_run_request( - schedule_id=schedule_id, - run_id=run_id, + _request = build_beta_toolboxes_update_request( + name=name, + content_type=content_type, api_version=self._config.api_version, + content=_content, headers=_headers, params=_params, ) @@ -9330,7 +16190,7 @@ def get_run(self, schedule_id: str, run_id: str, **kwargs: Any) -> _models.Sched if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.ScheduleRun, response.json()) + deserialized = _deserialize(_models.ToolboxObject, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -9338,32 +16198,15 @@ def get_run(self, schedule_id: str, run_id: str, **kwargs: Any) -> _models.Sched return deserialized # type: ignore @distributed_trace - def list_runs( - self, - schedule_id: str, - *, - type: Optional[Union[str, _models.ScheduleTaskType]] = None, - enabled: Optional[bool] = None, - **kwargs: Any - ) -> ItemPaged["_models.ScheduleRun"]: - """List all schedule runs. + def delete(self, name: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements + """Delete a toolbox and all its versions. - :param schedule_id: Identifier of the schedule. Required. - :type schedule_id: str - :keyword type: Filter by the type of schedule. Known values are: "Evaluation" and "Insight". - Default value is None. - :paramtype type: str or ~azure.ai.projects.models.ScheduleTaskType - :keyword enabled: Filter by the enabled status. Default value is None. - :paramtype enabled: bool - :return: An iterator like instance of ScheduleRun - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.ScheduleRun] + :param name: The name of the toolbox to delete. Required. + :type name: str + :return: None + :rtype: None :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[List[_models.ScheduleRun]] = kwargs.pop("cls", None) - error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -9372,196 +16215,52 @@ def list_runs( } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(next_link=None): - if not next_link: - - _request = build_beta_schedules_list_runs_request( - schedule_id=schedule_id, - type=type, - enabled=enabled, - 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) + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", - urllib.parse.urljoin(next_link, _parsed_next_link.path), - params=_next_request_params, - headers=_headers, - ) - 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) + cls: ClsType[None] = kwargs.pop("cls", None) - return _request + _request = build_beta_toolboxes_delete_request( + name=name, + 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) - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.ScheduleRun], - deserialized.get("value", []), - ) - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) - def get_next(next_link=None): - _request = prepare_request(next_link) + response = pipeline_response.http_response - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return ItemPaged(get_next, extract_data) - - -class BetaToolboxesOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`toolboxes` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - - @overload - def create_version( - self, - name: str, - *, - tools: List[_models.Tool], - content_type: str = "application/json", - description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - 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. - - :param name: The name of the toolbox. If the toolbox does not exist, it will be created. - Required. - :type name: str - :keyword tools: The list of tools to include in this version. Required. - :paramtype tools: list[~azure.ai.projects.models.Tool] - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :keyword description: A human-readable description of the toolbox. Default value is None. - :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the toolbox. Default value is - None. - :paramtype metadata: dict[str, str] - :keyword policies: Policy configuration for this toolbox version. Default value is None. - :paramtype policies: ~azure.ai.projects.models.ToolboxPolicies - :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxVersionObject - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - 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. - - :param name: The name of the toolbox. If the toolbox does not exist, it will be created. - Required. - :type name: str - :param body: Required. - :type body: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxVersionObject - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - 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. + raise HttpResponseError(response=response, model=error) - :param name: The name of the toolbox. If the toolbox does not exist, it will be created. - Required. - :type name: str - :param body: Required. - :type body: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxVersionObject - :raises ~azure.core.exceptions.HttpResponseError: - """ + if cls: + return cls(pipeline_response, None, {}) # type: ignore @distributed_trace - def create_version( - self, - name: str, - body: Union[JSON, IO[bytes]] = _Unset, - *, - tools: List[_models.Tool] = _Unset, - description: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - 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. + def delete_version( # pylint: disable=inconsistent-return-statements + self, name: str, version: str, **kwargs: Any + ) -> None: + """Delete a specific version of a toolbox. - :param name: The name of the toolbox. If the toolbox does not exist, it will be created. - Required. + :param name: The name of the toolbox. Required. :type name: str - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword tools: The list of tools to include in this version. Required. - :paramtype tools: list[~azure.ai.projects.models.Tool] - :keyword description: A human-readable description of the toolbox. Default value is None. - :paramtype description: str - :keyword metadata: Arbitrary key-value metadata to associate with the toolbox. Default value is - None. - :paramtype metadata: dict[str, str] - :keyword policies: Policy configuration for this toolbox version. Default value is None. - :paramtype policies: ~azure.ai.projects.models.ToolboxPolicies - :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxVersionObject + :param version: The version identifier to delete. Required. + :type version: str + :return: None + :rtype: None :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -9572,29 +16271,15 @@ def create_version( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.ToolboxVersionObject] = kwargs.pop("cls", None) - - if body is _Unset: - if tools is _Unset: - raise TypeError("missing required argument: tools") - body = {"description": description, "metadata": metadata, "policies": policies, "tools": tools} - body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[None] = kwargs.pop("cls", None) - _request = build_beta_toolboxes_create_version_request( + _request = build_beta_toolboxes_delete_version_request( name=name, - content_type=content_type, + version=version, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -9603,20 +16288,14 @@ def create_version( } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = False pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - 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 + if response.status_code not in [204]: map_error(status_code=response.status_code, response=response, error_map=error_map) error = _failsafe_deserialize( _models.ApiErrorResponse, @@ -9624,24 +16303,35 @@ def create_version( ) raise HttpResponseError(response=response, model=error) - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.ToolboxVersionObject, response.json()) - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, None, {}) # type: ignore - return deserialized # type: ignore + +class BetaSkillsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`skills` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") @distributed_trace - def get(self, name: str, **kwargs: Any) -> _models.ToolboxObject: - """Retrieve a toolbox. + def get(self, name: str, **kwargs: Any) -> _models.SkillDetails: + """Retrieves a skill. - :param name: The name of the toolbox to retrieve. Required. + :param name: The unique name of the skill. Required. :type name: str - :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxObject + :return: SkillDetails. The SkillDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillDetails :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -9655,9 +16345,9 @@ def get(self, name: str, **kwargs: Any) -> _models.ToolboxObject: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.ToolboxObject] = kwargs.pop("cls", None) + cls: ClsType[_models.SkillDetails] = kwargs.pop("cls", None) - _request = build_beta_toolboxes_get_request( + _request = build_beta_skills_get_request( name=name, api_version=self._config.api_version, headers=_headers, @@ -9692,7 +16382,7 @@ def get(self, name: str, **kwargs: Any) -> _models.ToolboxObject: if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.ToolboxObject, response.json()) + deserialized = _deserialize(_models.SkillDetails, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -9707,8 +16397,8 @@ def list( order: Optional[Union[str, _models.PageOrder]] = None, before: Optional[str] = None, **kwargs: Any - ) -> ItemPaged["_models.ToolboxObject"]: - """List all toolboxes. + ) -> ItemPaged["_models.SkillDetails"]: + """Returns the list of all skills. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -9724,14 +16414,14 @@ def list( 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: An iterator like instance of ToolboxObject - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.ToolboxObject] + :return: An iterator like instance of SkillDetails + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.SkillDetails] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.ToolboxObject]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.SkillDetails]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -9743,7 +16433,7 @@ def list( def prepare_request(_continuation_token=None): - _request = build_beta_toolboxes_list_request( + _request = build_beta_skills_list_request( limit=limit, order=order, after=_continuation_token, @@ -9761,7 +16451,7 @@ def prepare_request(_continuation_token=None): def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.ToolboxObject], + List[_models.SkillDetails], deserialized.get("data", []), ) if cls: @@ -9789,43 +16479,78 @@ def get_next(_continuation_token=None): return ItemPaged(get_next, extract_data) - @distributed_trace - def list_versions( - self, - name: str, - *, - limit: Optional[int] = None, - order: Optional[Union[str, _models.PageOrder]] = None, - before: Optional[str] = None, - **kwargs: Any - ) -> ItemPaged["_models.ToolboxVersionObject"]: - """List all versions of a toolbox. + @overload + def update( + self, name: str, *, default_version: str, content_type: str = "application/json", **kwargs: Any + ) -> _models.SkillDetails: + """Update a skill. - :param name: The name of the toolbox to list versions for. Required. + :param name: The name of the skill to update. Required. :type name: str - :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the - default is 20. Default value is None. - :paramtype limit: int - :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for - 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 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: An iterator like instance of ToolboxVersionObject - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.ToolboxVersionObject] + :keyword default_version: The version identifier that the skill should point to. When set, the + skill's default version will resolve to this version instead of the latest. Required. + :paramtype default_version: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: SkillDetails. The SkillDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillDetails :raises ~azure.core.exceptions.HttpResponseError: """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.ToolboxVersionObject]] = kwargs.pop("cls", None) + @overload + def update( + self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> _models.SkillDetails: + """Update a skill. + + :param name: The name of the skill to update. Required. + :type name: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: SkillDetails. The SkillDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def update( + self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.SkillDetails: + """Update a skill. + :param name: The name of the skill to update. Required. + :type name: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: SkillDetails. The SkillDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def update( + self, name: str, body: Union[JSON, IO[bytes]] = _Unset, *, default_version: str = _Unset, **kwargs: Any + ) -> _models.SkillDetails: + """Update a skill. + + :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. + :type body: JSON or IO[bytes] + :keyword default_version: The version identifier that the skill should point to. When set, the + skill's default version will resolve to this version instead of the latest. Required. + :paramtype default_version: str + :return: SkillDetails. The SkillDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -9834,65 +16559,76 @@ def list_versions( } error_map.update(kwargs.pop("error_map", {}) or {}) - def prepare_request(_continuation_token=None): + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} - _request = build_beta_toolboxes_list_versions_request( - name=name, - 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 + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.SkillDetails] = kwargs.pop("cls", None) - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = _deserialize( - List[_models.ToolboxVersionObject], - 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) + if body is _Unset: + if default_version is _Unset: + raise TypeError("missing required argument: default_version") + body = {"default_version": default_version} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - def get_next(_continuation_token=None): - _request = prepare_request(_continuation_token) + _request = build_beta_skills_update_request( + name=name, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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) - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs + _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 + ) + + response = pipeline_response.http_response + + 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, ) - response = pipeline_response.http_response + raise HttpResponseError(response=response, model=error) - 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 _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.SkillDetails, response.json()) - return pipeline_response + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore - return ItemPaged(get_next, extract_data) + return deserialized # type: ignore @distributed_trace - def get_version(self, name: str, version: str, **kwargs: Any) -> _models.ToolboxVersionObject: - """Retrieve a specific version of a toolbox. + def delete(self, name: str, **kwargs: Any) -> _models.DeleteSkillResult: + """Deletes a skill. - :param name: The name of the toolbox. Required. + :param name: The unique name of the skill. Required. :type name: str - :param version: The version identifier to retrieve. Required. - :type version: str - :return: ToolboxVersionObject. The ToolboxVersionObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxVersionObject + :return: DeleteSkillResult. The DeleteSkillResult is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DeleteSkillResult :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -9906,11 +16642,10 @@ def get_version(self, name: str, version: str, **kwargs: Any) -> _models.Toolbox _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.ToolboxVersionObject] = kwargs.pop("cls", None) + cls: ClsType[_models.DeleteSkillResult] = kwargs.pop("cls", None) - _request = build_beta_toolboxes_get_version_request( + _request = build_beta_skills_delete_request( name=name, - version=version, api_version=self._config.api_version, headers=_headers, params=_params, @@ -9944,7 +16679,7 @@ def get_version(self, name: str, version: str, **kwargs: Any) -> _models.Toolbox if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.ToolboxVersionObject, response.json()) + deserialized = _deserialize(_models.DeleteSkillResult, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -9952,75 +16687,91 @@ def get_version(self, name: str, version: str, **kwargs: Any) -> _models.Toolbox return deserialized # type: ignore @overload - def update( - self, name: str, *, default_version: str, content_type: str = "application/json", **kwargs: Any - ) -> _models.ToolboxObject: - """Update a toolbox to point to a specific version. + def create( + self, + name: str, + *, + content_type: str = "application/json", + inline_content: Optional[_models.SkillInlineContent] = None, + 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. - :param name: The name of the toolbox to update. Required. + :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str - :keyword default_version: The version identifier that the toolbox should point to. When set, - the toolbox's default version will resolve to this version instead of the latest. Required. - :paramtype default_version: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxObject + :keyword inline_content: Inline skill content for simple skills without file uploads. + Foundry-specific extension. Default value is None. + :paramtype inline_content: ~azure.ai.projects.models.SkillInlineContent + :keyword default: Whether to set this version as the default. Default value is None. + :paramtype default: bool + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def update( + def create( self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.ToolboxObject: - """Update a toolbox to point to a specific version. + ) -> _models.SkillVersion: + """Creates a new version of a skill. If the skill does not exist, it will be created. - :param name: The name of the toolbox to update. Required. + :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str :param body: Required. :type body: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxObject + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def update( + def create( self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.ToolboxObject: - """Update a toolbox to point to a specific version. + ) -> _models.SkillVersion: + """Creates a new version of a skill. If the skill does not exist, it will be created. - :param name: The name of the toolbox to update. Required. + :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str :param body: Required. :type body: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxObject + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def update( - self, name: str, body: Union[JSON, IO[bytes]] = _Unset, *, default_version: str = _Unset, **kwargs: Any - ) -> _models.ToolboxObject: - """Update a toolbox to point to a specific version. + def create( + self, + name: str, + body: Union[JSON, IO[bytes]] = _Unset, + *, + inline_content: Optional[_models.SkillInlineContent] = None, + 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. - :param name: The name of the toolbox to update. Required. + :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] - :keyword default_version: The version identifier that the toolbox should point to. When set, - the toolbox's default version will resolve to this version instead of the latest. Required. - :paramtype default_version: str - :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.ToolboxObject + :keyword inline_content: Inline skill content for simple skills without file uploads. + Foundry-specific extension. Default value is None. + :paramtype inline_content: ~azure.ai.projects.models.SkillInlineContent + :keyword default: Whether to set this version as the default. Default value is None. + :paramtype default: bool + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -10035,12 +16786,10 @@ def update( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.ToolboxObject] = kwargs.pop("cls", None) + cls: ClsType[_models.SkillVersion] = kwargs.pop("cls", None) if body is _Unset: - if default_version is _Unset: - raise TypeError("missing required argument: default_version") - body = {"default_version": default_version} + body = {"default": default, "inline_content": inline_content} body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None @@ -10049,7 +16798,7 @@ def update( else: _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_toolboxes_update_request( + _request = build_beta_skills_create_request( name=name, content_type=content_type, api_version=self._config.api_version, @@ -10086,21 +16835,53 @@ def update( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.ToolboxObject, response.json()) + deserialized = _deserialize(_models.SkillVersion, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + @overload + 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. + + :param name: The name of the skill. Required. + :type name: str + :param content: Required. + :type content: ~azure.ai.projects.models.CreateSkillVersionFromFilesBody + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @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. + + :param name: The name of the skill. Required. + :type name: str + :param content: Required. + :type content: JSON + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion + :raises ~azure.core.exceptions.HttpResponseError: + """ + @distributed_trace - def delete(self, name: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements - """Delete a toolbox and all its versions. + 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. - :param name: The name of the toolbox to delete. Required. + :param name: The name of the skill. Required. :type name: str - :return: None - :rtype: None + :param content: Is either a CreateSkillVersionFromFilesBody type or a JSON type. Required. + :type content: ~azure.ai.projects.models.CreateSkillVersionFromFilesBody or JSON + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -10114,11 +16895,17 @@ def delete(self, name: str, **kwargs: Any) -> None: # pylint: disable=inconsist _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[None] = kwargs.pop("cls", None) + cls: ClsType[_models.SkillVersion] = kwargs.pop("cls", None) - _request = build_beta_toolboxes_delete_request( + _body = content.as_dict() if isinstance(content, _Model) else content + _file_fields: list[str] = ["files"] + _data_fields: list[str] = ["default"] + _files = prepare_multipart_form_data(_body, _file_fields, _data_fields) + + _request = build_beta_skills_create_from_files_request( name=name, api_version=self._config.api_version, + files=_files, headers=_headers, params=_params, ) @@ -10127,14 +16914,20 @@ def delete(self, name: str, **kwargs: Any) -> None: # pylint: disable=inconsist } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _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 ) response = pipeline_response.http_response - if response.status_code not in [204]: + 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, @@ -10142,21 +16935,120 @@ def delete(self, name: str, **kwargs: Any) -> None: # pylint: disable=inconsist ) raise HttpResponseError(response=response, model=error) + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.SkillVersion, response.json()) + if cls: - return cls(pipeline_response, None, {}) # type: ignore + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore @distributed_trace - def delete_version( # pylint: disable=inconsistent-return-statements - self, name: str, version: str, **kwargs: Any - ) -> None: - """Delete a specific version of a toolbox. + def list_versions( + self, + name: str, + *, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_models.SkillVersion"]: + """List all versions of a skill. - :param name: The name of the toolbox. Required. + :param name: The name of the skill to list versions for. Required. :type name: str - :param version: The version identifier to delete. Required. + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + 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 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: An iterator like instance of SkillVersion + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.SkillVersion] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.SkillVersion]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_beta_skills_list_versions_request( + name=name, + 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 + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.SkillVersion], + 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) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def get_version(self, name: str, version: str, **kwargs: Any) -> _models.SkillVersion: + """Retrieve a specific version of a skill. + + :param name: The name of the skill. Required. + :type name: str + :param version: The version identifier to retrieve. Required. :type version: str - :return: None - :rtype: None + :return: SkillVersion. The SkillVersion is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.SkillVersion :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -10170,9 +17062,9 @@ def delete_version( # pylint: disable=inconsistent-return-statements _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[None] = kwargs.pop("cls", None) + cls: ClsType[_models.SkillVersion] = kwargs.pop("cls", None) - _request = build_beta_toolboxes_delete_version_request( + _request = build_beta_skills_get_version_request( name=name, version=version, api_version=self._config.api_version, @@ -10184,14 +17076,20 @@ def delete_version( # pylint: disable=inconsistent-return-statements } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _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 ) response = pipeline_response.http_response - if response.status_code not in [204]: + 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, @@ -10199,121 +17097,24 @@ def delete_version( # pylint: disable=inconsistent-return-statements ) raise HttpResponseError(response=response, model=error) - if cls: - return cls(pipeline_response, None, {}) # type: ignore - - -class BetaSkillsOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~azure.ai.projects.AIProjectClient`'s - :attr:`skills` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") - - @overload - def create( - self, - *, - name: str, - content_type: str = "application/json", - description: Optional[str] = None, - instructions: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - **kwargs: Any - ) -> _models.SkillObject: - """Creates a skill. - - :keyword name: The unique name of the skill. Required. - :paramtype name: str - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :keyword description: A human-readable description of the skill. Default value is None. - :paramtype description: str - :keyword instructions: Instructions that define the behavior of the skill. Default value is - None. - :paramtype instructions: str - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @overload - def create(self, body: JSON, *, content_type: str = "application/json", **kwargs: Any) -> _models.SkillObject: - """Creates a skill. - - :param body: Required. - :type body: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject - :raises ~azure.core.exceptions.HttpResponseError: - """ + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.SkillVersion, response.json()) - @overload - def create(self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any) -> _models.SkillObject: - """Creates a skill. + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore - :param body: Required. - :type body: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject - :raises ~azure.core.exceptions.HttpResponseError: - """ + return deserialized # type: ignore @distributed_trace - def create( - self, - body: Union[JSON, IO[bytes]] = _Unset, - *, - name: str = _Unset, - description: Optional[str] = None, - instructions: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, - **kwargs: Any - ) -> _models.SkillObject: - """Creates a skill. - - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword name: The unique name of the skill. Required. - :paramtype name: str - :keyword description: A human-readable description of the skill. Default value is None. - :paramtype description: str - :keyword instructions: Instructions that define the behavior of the skill. Default value is - None. - :paramtype instructions: str - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. + def download(self, name: str, **kwargs: Any) -> Iterator[bytes]: + """Download the zip content for the default version of a skill. - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :param name: The name of the skill. Required. + :type name: str + :return: Iterator[bytes] + :rtype: Iterator[bytes] :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -10324,28 +17125,14 @@ def create( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.SkillObject] = kwargs.pop("cls", None) - - if body is _Unset: - if name is _Unset: - raise TypeError("missing required argument: name") - body = {"description": description, "instructions": instructions, "metadata": metadata, "name": name} - body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/json" - _content = None - if isinstance(body, (IOBase, bytes)): - _content = body - else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + cls: ClsType[Iterator[bytes]] = kwargs.pop("cls", None) - _request = build_beta_skills_create_request( - content_type=content_type, + _request = build_beta_skills_download_request( + name=name, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -10355,14 +17142,14 @@ def create( _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = kwargs.pop("stream", True) pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - if response.status_code not in [201]: + if response.status_code not in [200]: if _stream: try: response.read() # Load the body in memory and close the socket @@ -10375,24 +17162,26 @@ def create( ) raise HttpResponseError(response=response, model=error) - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.SkillObject, response.json()) + response_headers = {} + response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, deserialized, response_headers) # type: ignore return deserialized # type: ignore @distributed_trace - def create_from_package(self, body: bytes, **kwargs: Any) -> _models.SkillObject: - """Creates a skill from a zip package. + def download_version(self, name: str, version: str, **kwargs: Any) -> Iterator[bytes]: + """Download the zip content for a specific version of a skill. - :param body: The zip package used to create the skill. Required. - :type body: bytes - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :param name: The name of the skill. Required. + :type name: str + :param version: The version to download content for. Required. + :type version: str + :return: Iterator[bytes] + :rtype: Iterator[bytes] :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -10403,18 +17192,15 @@ def create_from_package(self, body: bytes, **kwargs: Any) -> _models.SkillObject } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - content_type: str = kwargs.pop("content_type", _headers.pop("Content-Type", "application/zip")) - cls: ClsType[_models.SkillObject] = kwargs.pop("cls", None) - - _content = body + cls: ClsType[Iterator[bytes]] = kwargs.pop("cls", None) - _request = build_beta_skills_create_from_package_request( - content_type=content_type, + _request = build_beta_skills_download_version_request( + name=name, + version=version, api_version=self._config.api_version, - content=_content, headers=_headers, params=_params, ) @@ -10424,14 +17210,14 @@ def create_from_package(self, body: bytes, **kwargs: Any) -> _models.SkillObject _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = kwargs.pop("stream", True) pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) response = pipeline_response.http_response - if response.status_code not in [201]: + if response.status_code not in [200]: if _stream: try: response.read() # Load the body in memory and close the socket @@ -10444,24 +17230,27 @@ def create_from_package(self, body: bytes, **kwargs: Any) -> _models.SkillObject ) raise HttpResponseError(response=response, model=error) - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.SkillObject, response.json()) + response_headers = {} + response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, deserialized, response_headers) # type: ignore return deserialized # type: ignore @distributed_trace - def get(self, name: str, **kwargs: Any) -> _models.SkillObject: - """Retrieves a skill. + def delete_version(self, name: str, version: str, **kwargs: Any) -> _models.DeleteSkillVersionResponse: + """Delete a specific version of a skill. - :param name: The unique name of the skill. Required. + :param name: The name of the skill. Required. :type name: str - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :param version: The version identifier to delete. Required. + :type version: str + :return: DeleteSkillVersionResponse. The DeleteSkillVersionResponse is compatible with + MutableMapping + :rtype: ~azure.ai.projects.models.DeleteSkillVersionResponse :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -10475,10 +17264,11 @@ def get(self, name: str, **kwargs: Any) -> _models.SkillObject: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.SkillObject] = kwargs.pop("cls", None) + cls: ClsType[_models.DeleteSkillVersionResponse] = kwargs.pop("cls", None) - _request = build_beta_skills_get_request( + _request = build_beta_skills_delete_version_request( name=name, + version=version, api_version=self._config.api_version, headers=_headers, params=_params, @@ -10512,21 +17302,41 @@ def get(self, name: str, **kwargs: Any) -> _models.SkillObject: if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.SkillObject, response.json()) + deserialized = _deserialize(_models.DeleteSkillVersionResponse, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + +class BetaDatasetsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`datasets` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: AIProjectClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + @distributed_trace - def download(self, name: str, **kwargs: Any) -> Iterator[bytes]: - """Downloads a skill package. + def get_generation_job(self, job_id: str, **kwargs: Any) -> _models.DataGenerationJob: + """Get info about a data generation job. - :param name: The unique name of the skill. Required. - :type name: str - :return: Iterator[bytes] - :rtype: Iterator[bytes] + Gets the details of a data generation job by its ID. + + :param job_id: The ID of the job. Required. + :type job_id: str + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -10540,10 +17350,10 @@ def download(self, name: str, **kwargs: Any) -> Iterator[bytes]: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[Iterator[bytes]] = kwargs.pop("cls", None) + cls: ClsType[_models.DataGenerationJob] = kwargs.pop("cls", None) - _request = build_beta_skills_download_request( - name=name, + _request = build_beta_datasets_get_generation_job_request( + job_id=job_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -10554,7 +17364,7 @@ def download(self, name: str, **kwargs: Any) -> Iterator[bytes]: _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", True) + _stream = kwargs.pop("stream", False) pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) @@ -10575,9 +17385,12 @@ def download(self, name: str, **kwargs: Any) -> Iterator[bytes]: raise HttpResponseError(response=response, model=error) response_headers = {} - response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + response_headers["Retry-After"] = self._deserialize("int", response.headers.get("Retry-After")) - deserialized = response.iter_bytes() if _decompress else response.iter_raw() + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.DataGenerationJob, response.json()) if cls: return cls(pipeline_response, deserialized, response_headers) # type: ignore @@ -10585,15 +17398,17 @@ def download(self, name: str, **kwargs: Any) -> Iterator[bytes]: return deserialized # type: ignore @distributed_trace - def list( + def list_generation_jobs( self, *, limit: Optional[int] = None, order: Optional[Union[str, _models.PageOrder]] = None, before: Optional[str] = None, **kwargs: Any - ) -> ItemPaged["_models.SkillObject"]: - """Returns the list of all skills. + ) -> ItemPaged["_models.DataGenerationJob"]: + """Returns a list of data generation jobs. + + Returns a list of data generation jobs. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -10609,14 +17424,14 @@ def list( 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: An iterator like instance of SkillObject - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.SkillObject] + :return: An iterator like instance of DataGenerationJob + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.DataGenerationJob] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.SkillObject]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.DataGenerationJob]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -10628,7 +17443,7 @@ def list( def prepare_request(_continuation_token=None): - _request = build_beta_skills_list_request( + _request = build_beta_datasets_list_generation_jobs_request( limit=limit, order=order, after=_continuation_token, @@ -10646,7 +17461,7 @@ def prepare_request(_continuation_token=None): def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.SkillObject], + List[_models.DataGenerationJob], deserialized.get("data", []), ) if cls: @@ -10675,107 +17490,98 @@ def get_next(_continuation_token=None): return ItemPaged(get_next, extract_data) @overload - def update( + def create_generation_job( self, - name: str, + job: _models.DataGenerationJob, *, + operation_id: Optional[str] = None, content_type: str = "application/json", - description: Optional[str] = None, - instructions: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, **kwargs: Any - ) -> _models.SkillObject: - """Updates an existing skill. + ) -> _models.DataGenerationJob: + """Creates a data generation job. - :param name: The unique name of the skill. Required. - :type name: str + Creates a data generation job. + + :param job: The job to create. Required. + :type job: ~azure.ai.projects.models.DataGenerationJob + :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 :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :keyword description: A human-readable description of the skill. Default value is None. - :paramtype description: str - :keyword instructions: Instructions that define the behavior of the skill. Default value is - None. - :paramtype instructions: str - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def update( - self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> _models.SkillObject: - """Updates an existing skill. - - :param name: The unique name of the skill. Required. - :type name: str - :param body: Required. - :type body: JSON + 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. + + Creates a data generation job. + + :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 :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ @overload - def update( - self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> _models.SkillObject: - """Updates an existing skill. + def create_generation_job( + self, + job: IO[bytes], + *, + operation_id: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.DataGenerationJob: + """Creates a data generation job. - :param name: The unique name of the skill. Required. - :type name: str - :param body: Required. - :type body: IO[bytes] + Creates a data generation job. + + :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 :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace - def update( + def create_generation_job( self, - name: str, - body: Union[JSON, IO[bytes]] = _Unset, + job: Union[_models.DataGenerationJob, JSON, IO[bytes]], *, - description: Optional[str] = None, - instructions: Optional[str] = None, - metadata: Optional[dict[str, str]] = None, + operation_id: Optional[str] = None, **kwargs: Any - ) -> _models.SkillObject: - """Updates an existing skill. + ) -> _models.DataGenerationJob: + """Creates a data generation job. - :param name: The unique name of the skill. Required. - :type name: str - :param body: Is either a JSON type or a IO[bytes] type. Required. - :type body: JSON or IO[bytes] - :keyword description: A human-readable description of the skill. Default value is None. - :paramtype description: str - :keyword instructions: Instructions that define the behavior of the skill. Default value is - None. - :paramtype instructions: str - :keyword metadata: Set of 16 key-value pairs that can be attached to an object. This can be - useful for storing additional information about the object in a structured - format, and querying for objects via API or the dashboard. + Creates a data generation job. - Keys are strings with a maximum length of 64 characters. Values are strings - with a maximum length of 512 characters. Default value is None. - :paramtype metadata: dict[str, str] - :return: SkillObject. The SkillObject is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SkillObject + :param job: The job to create. Is one of the following types: DataGenerationJob, JSON, + IO[bytes] Required. + :type job: ~azure.ai.projects.models.DataGenerationJob 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 + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -10790,20 +17596,17 @@ def update( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.SkillObject] = kwargs.pop("cls", None) + cls: ClsType[_models.DataGenerationJob] = kwargs.pop("cls", None) - if body is _Unset: - body = {"description": description, "instructions": instructions, "metadata": metadata} - body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None - if isinstance(body, (IOBase, bytes)): - _content = body + if isinstance(job, (IOBase, bytes)): + _content = job else: - _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(job, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_beta_skills_update_request( - name=name, + _request = build_beta_datasets_create_generation_job_request( + operation_id=operation_id, content_type=content_type, api_version=self._config.api_version, content=_content, @@ -10823,7 +17626,7 @@ def update( response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [201]: if _stream: try: response.read() # Load the body in memory and close the socket @@ -10836,24 +17639,30 @@ def update( ) raise HttpResponseError(response=response, model=error) + response_headers = {} + response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) + response_headers["Location"] = self._deserialize("str", response.headers.get("Location")) + if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.SkillObject, response.json()) + deserialized = _deserialize(_models.DataGenerationJob, response.json()) if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return cls(pipeline_response, deserialized, response_headers) # type: ignore return deserialized # type: ignore @distributed_trace - def delete(self, name: str, **kwargs: Any) -> _models.DeleteSkillResponse: - """Deletes a skill. + def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.DataGenerationJob: + """Cancels a data generation job. - :param name: The unique name of the skill. Required. - :type name: str - :return: DeleteSkillResponse. The DeleteSkillResponse is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.DeleteSkillResponse + Cancels a data generation job by its ID. + + :param job_id: The ID of the job to cancel. Required. + :type job_id: str + :return: DataGenerationJob. The DataGenerationJob is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.DataGenerationJob :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -10867,10 +17676,10 @@ def delete(self, name: str, **kwargs: Any) -> _models.DeleteSkillResponse: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[_models.DeleteSkillResponse] = kwargs.pop("cls", None) + cls: ClsType[_models.DataGenerationJob] = kwargs.pop("cls", None) - _request = build_beta_skills_delete_request( - name=name, + _request = build_beta_datasets_cancel_generation_job_request( + job_id=job_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -10904,9 +17713,65 @@ def delete(self, name: str, **kwargs: Any) -> _models.DeleteSkillResponse: if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.DeleteSkillResponse, response.json()) + deserialized = _deserialize(_models.DataGenerationJob, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore return deserialized # type: ignore + + @distributed_trace + def delete_generation_job( # pylint: disable=inconsistent-return-statements + self, job_id: str, **kwargs: Any + ) -> None: + """Deletes a data generation job. + + Deletes a data generation job by its ID. + + :param job_id: The ID of the job to delete. Required. + :type job_id: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_beta_datasets_delete_generation_job_request( + job_id=job_id, + 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) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + 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, None, {}) # type: ignore diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch.py index c7c234ee5494..29adf947b535 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch.py @@ -18,13 +18,16 @@ from ._patch_telemetry import TelemetryOperations from ._patch_connections import ConnectionsOperations from ._patch_memories import BetaMemoryStoresOperations +from ._patch_models import BetaModelsOperations from ._patch_sessions import BetaAgentsOperations from ._operations import ( + BetaDatasetsOperations, BetaEvaluationTaxonomiesOperations, BetaEvaluatorsOperations, BetaInsightsOperations, BetaOperations as GeneratedBetaOperations, BetaRedTeamsOperations, + BetaRoutinesOperations, BetaSchedulesOperations, BetaSkillsOperations, BetaToolboxesOperations, @@ -104,14 +107,20 @@ class BetaOperations(GeneratedBetaOperations): """:class:`~azure.ai.projects.operations.BetaInsightsOperations` operations""" memory_stores: BetaMemoryStoresOperations """:class:`~azure.ai.projects.operations.BetaMemoryStoresOperations` operations""" + models: BetaModelsOperations + """:class:`~azure.ai.projects.operations.BetaModelsOperations` operations""" red_teams: BetaRedTeamsOperations """:class:`~azure.ai.projects.operations.BetaRedTeamsOperations` operations""" + routines: BetaRoutinesOperations + """:class:`~azure.ai.projects.operations.BetaRoutinesOperations` operations""" schedules: BetaSchedulesOperations """:class:`~azure.ai.projects.operations.BetaSchedulesOperations` operations""" toolboxes: BetaToolboxesOperations """:class:`~azure.ai.projects.operations.BetaToolboxesOperations` operations""" skills: BetaSkillsOperations """:class:`~azure.ai.projects.operations.BetaSkillsOperations` operations""" + datasets: BetaDatasetsOperations + """:class:`~azure.ai.projects.operations.BetaDatasetsOperations` operations""" def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -121,6 +130,8 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.agents = BetaAgentsOperations(self._client, self._config, self._serialize, self._deserialize) # Replace with patched class that includes begin_update_memories self.memory_stores = BetaMemoryStoresOperations(self._client, self._config, self._serialize, self._deserialize) + # Replace with patched class that includes create (3-step upload helper) + self.models = BetaModelsOperations(self._client, self._config, self._serialize, self._deserialize) for property_name, foundry_features_value in _BETA_OPERATION_FEATURE_HEADERS.items(): setattr( @@ -133,12 +144,15 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: __all__: List[str] = [ "AgentsOperations", "BetaAgentsOperations", + "BetaDatasetsOperations", "BetaEvaluationTaxonomiesOperations", "BetaEvaluatorsOperations", "BetaInsightsOperations", "BetaMemoryStoresOperations", + "BetaModelsOperations", "BetaOperations", "BetaRedTeamsOperations", + "BetaRoutinesOperations", "BetaSchedulesOperations", "BetaSkillsOperations", "BetaToolboxesOperations", diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_agents.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_agents.py index 02274e66fac2..a2f129f1dae1 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_agents.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_agents.py @@ -8,41 +8,17 @@ Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize """ -from typing import Union, Optional, Any, IO, overload, Final +from typing import Union, Optional, Any, IO, overload from azure.core.exceptions import HttpResponseError from azure.core.tracing.decorator import distributed_trace from ._operations import AgentsOperations as GeneratedAgentsOperations, JSON, _Unset from .. import models as _models -from ..models._enums import _AgentDefinitionOptInKeys -from ..models._patch import _FOUNDRY_FEATURES_HEADER_NAME, _has_header_case_insensitive - -""" -Example service response payload when the caller is trying to use a feature preview without opt-in flag (service error 403 (Forbidden)): - -"error": { - "code": "preview_feature_required", - "message": "Workflow agents is in preview. This operation requires the following opt-in preview feature(s): WorkflowAgents=V1Preview. Include the 'Foundry-Features: WorkflowAgents=V1Preview' header in your request.", - "param": "Foundry-Features", - "type": "invalid_request_error", - "details": [], - "additionalInfo": { - "request_id": "fdbc95804b7599404973026cd9ec732a" - } - } - -""" -_PREVIEW_FEATURE_REQUIRED_CODE: Final = "preview_feature_required" -_PREVIEW_FEATURE_ADDED_ERROR_MESSAGE: Final = ( - '\n**Python SDK users**: This operation requires you to set "allow_preview=True" ' - "when calling the AIProjectClient constructor. " - "\nNote that preview features are under development and subject to change. They should not be used in production environments." -) -_AGENT_OPERATION_FEATURE_HEADERS: Final[str] = ",".join( - [ - _AgentDefinitionOptInKeys.HOSTED_AGENTS_V1_PREVIEW.value, - _AgentDefinitionOptInKeys.WORKFLOW_AGENTS_V1_PREVIEW.value, - _AgentDefinitionOptInKeys.AGENT_ENDPOINT_V1_PREVIEW.value, - ] +from ..models._patch import ( + _FOUNDRY_FEATURES_HEADER_NAME, + _has_header_case_insensitive, + _AGENT_OPERATION_FEATURE_HEADERS, + _PREVIEW_FEATURE_REQUIRED_CODE, + _PREVIEW_FEATURE_ADDED_ERROR_MESSAGE, ) @@ -65,6 +41,7 @@ def create_version( content_type: str = "application/json", metadata: Optional[dict[str, str]] = None, description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, **kwargs: Any, ) -> _models.AgentVersionDetails: """Create a new agent version. @@ -91,6 +68,8 @@ def create_version( :paramtype metadata: dict[str, str] :keyword description: A human-readable description of the agent. Default value is None. :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.projects.models.AgentBlueprintReference :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping :rtype: ~azure.ai.projects.models.AgentVersionDetails :raises ~azure.core.exceptions.HttpResponseError: @@ -154,6 +133,7 @@ def create_version( definition: _models.AgentDefinition = _Unset, metadata: Optional[dict[str, str]] = None, description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, **kwargs: Any, ) -> _models.AgentVersionDetails: """Create a new agent version. @@ -179,6 +159,8 @@ def create_version( :paramtype metadata: dict[str, str] :keyword description: A human-readable description of the agent. Default value is None. :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.projects.models.AgentBlueprintReference :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping :rtype: ~azure.ai.projects.models.AgentVersionDetails :raises ~azure.core.exceptions.HttpResponseError: @@ -200,9 +182,25 @@ def create_version( definition=definition, metadata=metadata, description=description, + blueprint_reference=blueprint_reference, **kwargs, ) except HttpResponseError as exc: + """ + Example service response payload when the caller is trying to use a feature preview without opt-in flag (service error 403 (Forbidden)): + + "error": { + "code": "preview_feature_required", + "message": "Workflow agents is in preview. This operation requires the following opt-in preview feature(s): WorkflowAgents=V1Preview. Include the 'Foundry-Features: WorkflowAgents=V1Preview' header in your request.", + "param": "Foundry-Features", + "type": "invalid_request_error", + "details": [], + "additionalInfo": { + "request_id": "fdbc95804b7599404973026cd9ec732a" + } + } + + """ if exc.status_code == 403 and not self._config.allow_preview and exc.model is not None: api_error_response = exc.model if hasattr(api_error_response, "error") and api_error_response.error is not None: diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_evaluation_rules.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_evaluation_rules.py index 19dcee1ed6bd..859bea44b87b 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_evaluation_rules.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_evaluation_rules.py @@ -12,10 +12,14 @@ from azure.core.exceptions import HttpResponseError from azure.core.tracing.decorator import distributed_trace from ._operations import EvaluationRulesOperations as GeneratedEvaluationRulesOperations, JSON -from ._patch_agents import _PREVIEW_FEATURE_REQUIRED_CODE, _PREVIEW_FEATURE_ADDED_ERROR_MESSAGE from .. import models as _models from ..models._enums import _FoundryFeaturesOptInKeys -from ..models._patch import _FOUNDRY_FEATURES_HEADER_NAME, _has_header_case_insensitive +from ..models._patch import ( + _FOUNDRY_FEATURES_HEADER_NAME, + _has_header_case_insensitive, + _PREVIEW_FEATURE_REQUIRED_CODE, + _PREVIEW_FEATURE_ADDED_ERROR_MESSAGE, +) class EvaluationRulesOperations(GeneratedEvaluationRulesOperations): diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_models.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_models.py new file mode 100644 index 000000000000..7cd83ca71a8e --- /dev/null +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_models.py @@ -0,0 +1,369 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" + +import logging +import os +import shutil +import subprocess +import time +from pathlib import Path +from typing import Any, Literal, Optional, Union, overload + +from azure.core.exceptions import ResourceNotFoundError +from azure.core.tracing.decorator import distributed_trace + +from ._operations import BetaModelsOperations as BetaModelsOperationsGenerated +from ..models._models import ( + ModelPendingUploadRequest, + ModelPendingUploadResponse, + ModelVersion, + PendingUploadType, +) + +logger = logging.getLogger(__name__) + + +class BetaModelsOperations(BetaModelsOperationsGenerated): + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.projects.AIProjectClient`'s + :attr:`beta.models ` attribute. + """ + + @staticmethod + def _extract_pending_upload_targets( + response: Union[ModelPendingUploadResponse, dict], + ) -> "tuple[str, str, Optional[str]]": + """Return ``(sas_uri, container_blob_uri, pending_upload_id)`` from a pending-upload response. + + The service currently returns the raw datastore-style payload + (``blobReferenceForConsumption`` / ``temporaryDataReferenceId``) for some + Foundry deployments rather than the SDK-modeled ``ModelPendingUploadResponse`` + shape (``blobReference`` / ``pendingUploadId``). Tolerate both wire + shapes so callers don't have to. + + :param response: The pending-upload response from the service. + :type response: ~azure.ai.projects.models.ModelPendingUploadResponse or dict + :return: A tuple of ``(sas_uri, container_blob_uri, pending_upload_id)``. + :rtype: tuple[str, str, str or None] + """ + payload = dict(response) if isinstance(response, dict) else response.as_dict() + + blob_ref = payload.get("blobReferenceForConsumption") or payload.get("blobReference") or {} + sas_uri = (blob_ref.get("credential") or {}).get("sasUri") + container_blob_uri = blob_ref.get("blobUri") + pending_upload_id = payload.get("temporaryDataReferenceId") or payload.get("pendingUploadId") + + if not sas_uri or not container_blob_uri: + raise ValueError("Could not locate SAS URI / blob URI in pending_upload response: " f"{payload!r}") + return sas_uri, container_blob_uri, pending_upload_id + + @staticmethod + def _resolve_azcopy(azcopy_path: Optional[str] = None) -> str: + """Locate the ``azcopy`` executable or raise ``RuntimeError``. + + :param azcopy_path: Optional explicit path to the azcopy executable. + Defaults to ``shutil.which("azcopy")``. + :type azcopy_path: str or None + :return: Absolute path to the resolved azcopy executable. + :rtype: str + """ + azcopy = azcopy_path or shutil.which("azcopy") + if not azcopy: + raise RuntimeError( + "`azcopy` was not found on PATH. Install AzCopy " + "(https://aka.ms/downloadazcopy) and ensure it is on PATH, or " + "pass `azcopy_path=` explicitly." + ) + return azcopy + + @staticmethod + def _validate_create_inputs( + *, + name: str, + version: str, + source: Union[str, "os.PathLike[str]"], + azcopy_path: Optional[str], + wait_for_commit: bool, + polling_timeout: float, + polling_interval: float, + ) -> Path: + """Validate ``create`` inputs up-front, before any service call. + + Returns the resolved ``Path`` for ``source``. Raises ``ValueError`` for + bad inputs and ``RuntimeError`` if ``azcopy`` cannot be located. + + :keyword name: Name of the model to register. + :paramtype name: str + :keyword version: Version identifier for the model. + :paramtype version: str + :keyword source: Local file or directory containing the model weights. + :paramtype source: str or os.PathLike[str] + :keyword azcopy_path: Optional explicit path to the azcopy executable. + :paramtype azcopy_path: str or None + :keyword wait_for_commit: Whether to poll for commit completion. + :paramtype wait_for_commit: bool + :keyword polling_timeout: Total seconds to poll for commit completion. + :paramtype polling_timeout: float + :keyword polling_interval: Seconds between poll attempts. + :paramtype polling_interval: float + :return: The resolved ``Path`` for ``source``. + :rtype: pathlib.Path + """ + if not isinstance(name, str) or not name.strip(): + raise ValueError("`name` must be a non-empty string.") + if not isinstance(version, str) or not version.strip(): + raise ValueError("`version` must be a non-empty string.") + + source_path = Path(os.fspath(source)) + if not source_path.exists(): + raise ValueError(f"Upload source does not exist: {source_path}") + if source_path.is_dir() and not any(p.is_file() for p in source_path.rglob("*")): + raise ValueError(f"Upload source directory is empty: {source_path}") + if source_path.is_file() and source_path.stat().st_size == 0: + raise ValueError(f"Upload source file is empty: {source_path}") + + if wait_for_commit: + if polling_timeout <= 0: + raise ValueError("`polling_timeout` must be > 0 when `wait_for_commit` is True.") + if polling_interval <= 0: + raise ValueError("`polling_interval` must be > 0 when `wait_for_commit` is True.") + + # Fail fast if azcopy isn't installed, before we provision a SAS container. + BetaModelsOperations._resolve_azcopy(azcopy_path) + return source_path + + @staticmethod + def _run_azcopy(source: Path, sas_uri: str, *, azcopy_path: Optional[str] = None) -> None: + """Shell out to ``azcopy copy`` to upload ``source`` to the SAS container. + + :param source: Local file or directory to upload. + :type source: pathlib.Path + :param sas_uri: SAS URI for the destination container. + :type sas_uri: str + :keyword azcopy_path: Optional explicit path to the azcopy executable. + :paramtype azcopy_path: str or None + """ + azcopy = BetaModelsOperations._resolve_azcopy(azcopy_path) + + if source.is_dir(): + src_arg = str(source / "*") + elif source.is_file(): + src_arg = str(source) + else: + raise ValueError(f"Upload source does not exist: {source}") + + cmd = [ + azcopy, + "copy", + src_arg, + sas_uri, + "--from-to", + "LocalBlob", + "--recursive", + ] + + # Don't log the SAS query string — it's a credential. + redacted = cmd.copy() + redacted[3] = sas_uri.split("?", 1)[0] + "?" + logger.info("[create] running: %s", " ".join(redacted)) + + completed = subprocess.run(cmd, check=False, capture_output=True, text=True) + if completed.stdout: + logger.debug("[create] azcopy stdout:\n%s", completed.stdout) + if completed.stderr: + logger.debug("[create] azcopy stderr:\n%s", completed.stderr) + if completed.returncode != 0: + raise RuntimeError( + f"azcopy exited with code {completed.returncode}.\n" + f"stdout:\n{completed.stdout}\nstderr:\n{completed.stderr}" + ) + + @overload + def create( + self, + *, + name: str, + version: str, + source: Union[str, "os.PathLike[str]"], + weight_type: Optional[str] = None, + base_model: Optional[str] = None, + description: Optional[str] = None, + tags: Optional["dict[str, str]"] = None, + azcopy_path: Optional[str] = None, + wait_for_commit: Literal[True] = True, + polling_timeout: float = 300.0, + polling_interval: float = 2.0, + **kwargs: Any, + ) -> ModelVersion: ... + + @overload + def create( + self, + *, + name: str, + version: str, + source: Union[str, "os.PathLike[str]"], + weight_type: Optional[str] = None, + base_model: Optional[str] = None, + description: Optional[str] = None, + tags: Optional["dict[str, str]"] = None, + azcopy_path: Optional[str] = None, + wait_for_commit: Literal[False], + polling_timeout: float = 300.0, + polling_interval: float = 2.0, + **kwargs: Any, + ) -> None: ... + + @distributed_trace + def create( + self, + *, + name: str, + version: str, + source: Union[str, "os.PathLike[str]"], + weight_type: Optional[str] = None, + base_model: Optional[str] = None, + description: Optional[str] = None, + tags: Optional["dict[str, str]"] = None, + azcopy_path: Optional[str] = None, + wait_for_commit: bool = True, + polling_timeout: float = 300.0, + polling_interval: float = 2.0, + **kwargs: Any, + ) -> Optional[ModelVersion]: + """Register a local model by running the full upload-first sequence. + + This wraps the three mandatory steps of the model-registration spec + into a single call: + + 1. :meth:`pending_upload` — provision a project-managed blob container + and obtain a SAS URI. + 2. ``azcopy copy`` — upload the local weight files directly to the + SAS container. + 3. :meth:`pending_create_version` — finalize registration with the + ``ModelVersion`` body (``blob_uri``, ``weight_type``, ``base_model``, + ``description``, ``tags``). + + :keyword name: Name of the model to register. Required. + :paramtype name: str + :keyword version: Version identifier for the model. Required. + :paramtype version: str + :keyword source: Local file or directory containing the model weights. + If a directory, its contents are uploaded recursively to the SAS + container root. Required. + :paramtype source: str or os.PathLike[str] + :keyword weight_type: Optional weight type (e.g. ``"FullWeight"``, + ``"LoRA"``, ``"DraftModel"``). + :paramtype weight_type: str + :keyword base_model: Optional base model asset ID. + :paramtype base_model: str + :keyword description: Optional asset description. + :paramtype description: str + :keyword tags: Optional asset tags. + :paramtype tags: dict[str, str] + :keyword azcopy_path: Optional explicit path to the azcopy executable. + Defaults to ``shutil.which("azcopy")``. + :paramtype azcopy_path: str + :keyword wait_for_commit: When True (default) poll :meth:`get` until + the committed ``ModelVersion`` is observable, and return it. + When False, return ``None`` after the async commit is accepted. + :paramtype wait_for_commit: bool + :keyword polling_timeout: Total seconds to poll for commit completion. + :paramtype polling_timeout: float + :keyword polling_interval: Seconds between poll attempts. + :paramtype polling_interval: float + :return: The committed :class:`~azure.ai.projects.models.ModelVersion` + when ``wait_for_commit`` is True, otherwise ``None``. + :rtype: ~azure.ai.projects.models.ModelVersion or None + :raises ValueError: If ``name``/``version`` are empty, ``source`` does + not exist or is empty, polling parameters are non-positive, or the + pending-upload response is missing the SAS / blob URI. + :raises RuntimeError: If ``azcopy`` is not on PATH or exits with a + non-zero status, or the registration does not commit before + ``polling_timeout`` elapses. + """ + # --- Step 0: validate inputs up-front -------------------------------- + # Cheap local checks so we don't provision a SAS container or run + # azcopy when something obviously wrong was passed in. + source_path = self._validate_create_inputs( + name=name, + version=version, + source=source, + azcopy_path=azcopy_path, + wait_for_commit=wait_for_commit, + polling_timeout=polling_timeout, + polling_interval=polling_interval, + ) + + # --- Step 1: StartPendingUpload -------------------------------------- + logger.info( + "[create] step 1/3 pending_upload(name=%r, version=%r)", + name, + version, + ) + pending = self.pending_upload( + name=name, + version=version, + pending_upload_request=ModelPendingUploadRequest( + pending_upload_type=PendingUploadType.TEMPORARY_BLOB_REFERENCE, + ), + **kwargs, + ) + sas_uri, container_blob_uri, pending_upload_id = self._extract_pending_upload_targets(pending) + logger.info( + "[create] pending_upload_id=%s blob_uri=%s", + pending_upload_id, + container_blob_uri, + ) + + # --- Step 2: Upload via azcopy --------------------------------------- + logger.info("[create] step 2/3 azcopy upload from %s", source_path) + self._run_azcopy(source_path, sas_uri, azcopy_path=azcopy_path) + + # --- Step 3: Commit registration ------------------------------------- + model_version_body = ModelVersion( + blob_uri=container_blob_uri, + weight_type=weight_type, + base_model=base_model, + description=description, + tags=tags or {}, + ) + logger.info( + "[create] step 3/3 pending_create_version(name=%r, version=%r)", + name, + version, + ) + self.pending_create_version(name=name, version=version, model_version=model_version_body, **kwargs) + + if not wait_for_commit: + return None + + # The async op returns 202; the service materializes the ModelVersion + # asynchronously. Poll get() until it appears or we time out. + deadline = time.monotonic() + polling_timeout + last_exc: Optional[BaseException] = None + while True: + try: + return self.get(name=name, version=version, **kwargs) + except ResourceNotFoundError as ex: + last_exc = ex + if time.monotonic() >= deadline: + raise RuntimeError( + f"Model {name!r}@{version!r} did not appear within " + f"{polling_timeout}s after pending_create_version." + ) from last_exc + time.sleep(polling_interval) + + +__all__ = ["BetaModelsOperations"] diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_sessions.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_sessions.py index 053bbc8292ed..8d417620e725 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_sessions.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_sessions.py @@ -34,7 +34,7 @@ def upload_session_file( # type: ignore[override] *, path: str, **kwargs: Any, - ) -> _models.SessionFileWriteResponse: + ) -> _models.SessionFileWriteResult: """Upload a file to the session sandbox. Accepts either a ``bytes`` buffer or a local file path (``str``). @@ -52,9 +52,9 @@ def upload_session_file( # type: ignore[override] :keyword path: The destination file path within the sandbox, relative to the session home directory. Required. :paramtype path: str - :return: SessionFileWriteResponse. The SessionFileWriteResponse is compatible with + :return: SessionFileWriteResult. The SessionFileWriteResult is compatible with MutableMapping - :rtype: ~azure.ai.projects.models.SessionFileWriteResponse + :rtype: ~azure.ai.projects.models.SessionFileWriteResult :raises ~azure.core.exceptions.HttpResponseError: :raises FileNotFoundError: If *content_or_file_path* is a ``str`` and the file does not exist. """ diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_ai_project_instrumentor.py b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_ai_project_instrumentor.py index 1a22ca314704..c23a994b795e 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_ai_project_instrumentor.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_ai_project_instrumentor.py @@ -645,7 +645,7 @@ def _add_message_event( # pylint: disable=too-many-branches,too-many-statements attribute_name = GEN_AI_INPUT_MESSAGES # Set the attribute on the span - if span and span.span_instance.is_recording: + if span and span.span_instance.is_recording(): span.add_attribute(attribute_name, message_json) def _get_field(self, obj: Any, field: str) -> Any: @@ -722,7 +722,7 @@ def _add_instructions_event( # Use attributes for instructions tracing # System instructions format: array of content objects without role/parts wrapper message_json = json.dumps(content_array, ensure_ascii=False) - if span and span.span_instance.is_recording: + if span and span.span_instance.is_recording(): span.add_attribute(GEN_AI_SYSTEM_MESSAGE, message_json) def _status_to_string(self, status: Any) -> str: @@ -782,7 +782,7 @@ def start_create_agent_span( # pylint: disable=too-many-locals reasoning_summary=reasoning_summary, structured_inputs=(str(structured_inputs) if structured_inputs is not None else None), ) - if span and span.span_instance.is_recording: + if span and span.span_instance.is_recording(): span.add_attribute(GEN_AI_OPERATION_NAME, OperationName.CREATE_AGENT.value) if name: span.add_attribute(GEN_AI_AGENT_NAME, name) @@ -842,7 +842,7 @@ def start_create_thread_span( # _tool_resources: Optional["ToolResources"] = None, ) -> "Optional[AbstractSpan]": span = start_span(OperationName.CREATE_THREAD, server_address=server_address, port=port) - if span and span.span_instance.is_recording: + if span and span.span_instance.is_recording(): for message in messages or []: self.add_thread_message_event(span, message) diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_responses_instrumentor.py b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_responses_instrumentor.py index 95cb28183b35..fdb3cc456214 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_responses_instrumentor.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_responses_instrumentor.py @@ -523,7 +523,7 @@ def _set_attributes(self, span: "AbstractSpan", *attrs: Tuple[str, Any]) -> None def _set_span_attribute_safe(self, span: "AbstractSpan", key: str, value: Any) -> None: """Safely set a span attribute only if the value is meaningful.""" - if not span or not span.span_instance.is_recording: + if not span or not span.span_instance.is_recording(): return # Only set attribute if value exists and is meaningful @@ -846,7 +846,7 @@ def _add_workflow_action_events( conversation_id: Optional[str] = None, ) -> None: """Add workflow action events to the span for workflow agents.""" - if not span or not span.span_instance.is_recording: + if not span or not span.span_instance.is_recording(): return # Check if response has output items @@ -1149,7 +1149,7 @@ def _add_tool_call_events( # pylint: disable=too-many-branches conversation_id: Optional[str] = None, ) -> None: """Add tool call events to the span from response output.""" - if not span or not span.span_instance.is_recording: + if not span or not span.span_instance.is_recording(): return # Extract function calls and tool calls from response output @@ -1638,7 +1638,7 @@ def start_responses_span( gen_ai_provider=RESPONSES_PROVIDER, ) - if span and span.span_instance.is_recording: + if span and span.span_instance.is_recording(): # Set operation name attribute (start_span doesn't set this automatically) self._set_attributes( span, @@ -2614,7 +2614,7 @@ def cleanup(self): # Join all accumulated output content complete_content = "".join(self.accumulated_output) - if self.span.span_instance.is_recording: + if self.span.span_instance.is_recording(): # Add tool call events if we detected any output items (tool calls, etc.) if self.has_output_items: # Create mock response with output items for event generation @@ -2721,7 +2721,7 @@ def __init__( ) # End span with proper status - if self.span.span_instance.is_recording: + if self.span.span_instance.is_recording(): self.span.span_instance.set_status( # pyright: ignore [reportPossiblyUnboundVariable] StatusCode.OK @@ -2764,7 +2764,7 @@ def __next__(self): span_attributes=span_attributes, error_type=str(type(e).__name__), ) - if self.span.span_instance.is_recording: + if self.span.span_instance.is_recording(): self.span.span_instance.set_status( # pyright: ignore [reportPossiblyUnboundVariable] StatusCode.ERROR, @@ -2791,7 +2791,7 @@ def _finalize_span(self): span_attributes=span_attributes, ) - if self.span.span_instance.is_recording: + if self.span.span_instance.is_recording(): # Note: For streaming responses, response metadata like tokens, finish_reasons # are typically not available in individual chunks, so we focus on content. @@ -3092,7 +3092,7 @@ def cleanup(self): # Join all accumulated output content complete_content = "".join(self.accumulated_output) - if self.span.span_instance.is_recording: + if self.span.span_instance.is_recording(): # Add tool call events if we detected any output items (tool calls, etc.) if self.has_output_items: # Create mock response with output items for event generation @@ -3199,7 +3199,7 @@ def __init__( ) # End span with proper status - if self.span.span_instance.is_recording: + if self.span.span_instance.is_recording(): self.span.span_instance.set_status( # pyright: ignore [reportPossiblyUnboundVariable] StatusCode.OK @@ -3241,7 +3241,7 @@ async def __anext__(self): span_attributes=span_attributes, error_type=str(type(e).__name__), ) - if self.span.span_instance.is_recording: + if self.span.span_instance.is_recording(): self.span.span_instance.set_status( # pyright: ignore [reportPossiblyUnboundVariable] StatusCode.ERROR, @@ -3268,7 +3268,7 @@ def _finalize_span(self): span_attributes=span_attributes, ) - if self.span.span_instance.is_recording: + if self.span.span_instance.is_recording(): # Note: For streaming responses, response metadata like tokens, finish_reasons # are typically not available in individual chunks, so we focus on content. @@ -3407,7 +3407,7 @@ def start_create_conversation_span( gen_ai_provider=RESPONSES_PROVIDER, ) - if span and span.span_instance.is_recording: + if span and span.span_instance.is_recording(): self._set_span_attribute_safe(span, GEN_AI_OPERATION_NAME, OperationName.CREATE_CONVERSATION.value) return span @@ -3605,7 +3605,7 @@ def start_list_conversation_items_span( gen_ai_provider=RESPONSES_PROVIDER, ) - if span and span.span_instance.is_recording: + if span and span.span_instance.is_recording(): # Set operation name attribute (start_span doesn't set this automatically) self._set_attributes( span, @@ -3624,7 +3624,7 @@ def _add_conversation_item_event( # pylint: disable=too-many-branches,too-many- item: Any, ) -> None: """Add a conversation item event to the span.""" - if not span or not span.span_instance.is_recording: + if not span or not span.span_instance.is_recording(): return # Extract basic item information diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_utils.py b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_utils.py index 931c3d2abf7b..47047e2720c3 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_utils.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_utils.py @@ -223,7 +223,7 @@ def start_span( schema_version=GEN_AI_SEMANTIC_CONVENTIONS_SCHEMA_VERSION, ) - if span and span.span_instance.is_recording: + if span and span.span_instance.is_recording(): span.add_attribute(AZ_NAMESPACE, AZ_NAMESPACE_VALUE) span.add_attribute(GEN_AI_PROVIDER_NAME, AGENTS_PROVIDER) diff --git a/sdk/ai/azure-ai-projects/cspell.json b/sdk/ai/azure-ai-projects/cspell.json index b3584c59e861..dd14b6b68b0e 100644 --- a/sdk/ai/azure-ai-projects/cspell.json +++ b/sdk/ai/azure-ai-projects/cspell.json @@ -6,12 +6,15 @@ "aiservices", "ansii", "aread", + "azcopy", + "Azcopy", "azureai", "azureopenai", "balapvbyostoragecanary", "closefd", "cogsvc", "CSDL", + "dargilco", "dedup", "evals", "FineTuning", @@ -27,12 +30,15 @@ "Ministral", "mpkjc", "quantitive", + "recsmplmdl", "reraises", + "simpleqna", + "skoid", "Tadmaq", "Udbk", "UPIA", - "xhigh", - "Vnext" + "Vnext", + "xhigh" ], "ignorePaths": [ "*.csv", diff --git a/sdk/ai/azure-ai-projects/dev_requirements.txt b/sdk/ai/azure-ai-projects/dev_requirements.txt index 3a0781d99156..6641c1e8f14a 100644 --- a/sdk/ai/azure-ai-projects/dev_requirements.txt +++ b/sdk/ai/azure-ai-projects/dev_requirements.txt @@ -8,11 +8,13 @@ azure-core-tracing-opentelemetry azure-mgmt-authorization azure-mgmt-cognitiveservices azure-mgmt-resource +azure-mgmt-subscription azure-monitor-opentelemetry azure-monitor-query jsonref opentelemetry-sdk python-dotenv +black # Can't include those, because they are not supported in Python 3.9. Samples that use these package # cannot be run as pytest, because the pipeline will fail on Python 3.9 jobs. # pillow diff --git a/sdk/ai/azure-ai-projects/docs/subclients.md b/sdk/ai/azure-ai-projects/docs/subclients.md new file mode 100644 index 000000000000..11343e0fbd61 --- /dev/null +++ b/sdk/ai/azure-ai-projects/docs/subclients.md @@ -0,0 +1,194 @@ +# AIProjectClient Subclients + +This document lists all sub-clients available on `AIProjectClient` and their public method counts. Overload methods are not counted. Only synchronous methods are counted (but each one has an equivalent asynchronous method). + +## Summary + +There are a total of 132 unique public methods across all sub-clients. + +### Top-level Sub-clients (stable operations) + +| Subclient | Class Name | Methods Count | +|-----------|------------|----------------| +| `agents` | AgentsOperations | 8 | +| `evaluation_rules` | EvaluationRulesOperations | 4 | +| `connections` | ConnectionsOperations | 3 | +| `datasets` | DatasetsOperations | 9 | +| `deployments` | DeploymentsOperations | 2 | +| `indexes` | IndexesOperations | 5 | +| `telemetry` | TelemetryOperations | 1 | + +### Nested Sub-clients (beta operations) + +| Subclient | Class Name | Methods Count | +|-----------|------------|----------------| +| `beta.agents` | BetaAgentsOperations | 21 | +| `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.red_teams` | BetaRedTeamsOperations | 3 | +| `beta.routines` | BetaRoutinesOperations | 8 | +| `beta.schedules` | BetaSchedulesOperations | 6 | +| `beta.skills` | BetaSkillsOperations | 7 | +| `beta.toolboxes` | BetaToolboxesOperations | 8 | + + +## Method list table + +Alphabetically sorted, with ".beta" sub-client at the end. + +``` +.agents.create_version +.agents.create_version_from_manifest +.agents.delete +.agents.delete_version +.agents.get +.agents.get_version +.agents.list +.agents.list_versions + +.connections.get +.connections.get_default +.connections.list + +.datasets.create_or_update +.datasets.delete +.datasets.get +.datasets.get_credentials +.datasets.list +.datasets.list_versions +.datasets.pending_upload +.datasets.upload_file +.datasets.upload_folder + +.deployments.get +.deployments.list + +.evaluation_rules.create_or_update +.evaluation_rules.delete +.evaluation_rules.get +.evaluation_rules.list + +.indexes.create_or_update +.indexes.delete +.indexes.get +.indexes.list +.indexes.list_versions + +.telemetry.get_application_insights_connection_string + +.beta.agents.cancel_optimization_job +.beta.agents.create_optimization_job +.beta.agents.create_session +.beta.agents.create_version_from_code +.beta.agents.delete_optimization_job +.beta.agents.delete_session +.beta.agents.delete_session_file +.beta.agents.download_code +.beta.agents.download_session_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_files +.beta.agents.get_session_log_stream +.beta.agents.list_optimization_candidates +.beta.agents.list_optimization_jobs +.beta.agents.list_sessions +.beta.agents.patch_agent_details +.beta.agents.upload_session_file + +.beta.datasets.cancel_generation_job +.beta.datasets.create_generation_job +.beta.datasets.delete_generation_job +.beta.datasets.get_generation_job +.beta.datasets.list_generation_jobs + +.beta.evaluation_taxonomies.create +.beta.evaluation_taxonomies.delete +.beta.evaluation_taxonomies.get +.beta.evaluation_taxonomies.list +.beta.evaluation_taxonomies.update + +.beta.evaluators.cancel_generation_job +.beta.evaluators.create_generation_job +.beta.evaluators.create_version +.beta.evaluators.delete_generation_job +.beta.evaluators.delete_version +.beta.evaluators.get_credentials +.beta.evaluators.get_generation_job +.beta.evaluators.get_version +.beta.evaluators.list +.beta.evaluators.list_generation_jobs +.beta.evaluators.list_versions +.beta.evaluators.pending_upload +.beta.evaluators.update_version + +.beta.insights.generate +.beta.insights.get +.beta.insights.list + +.beta.memory_stores.begin_update_memories +.beta.memory_stores.create +.beta.memory_stores.create_memory +.beta.memory_stores.delete +.beta.memory_stores.delete_memory +.beta.memory_stores.delete_scope +.beta.memory_stores.get +.beta.memory_stores.get_memory +.beta.memory_stores.list +.beta.memory_stores.list_memories +.beta.memory_stores.search_memories +.beta.memory_stores.update +.beta.memory_stores.update_memory + +.beta.models.pending_create_version +.beta.models.delete +.beta.models.get +.beta.models.get_credentials +.beta.models.list +.beta.models.list_versions +.beta.models.pending_upload +.beta.models.update + +.beta.red_teams.create +.beta.red_teams.get +.beta.red_teams.list + +.beta.routines.create_or_update +.beta.routines.delete +.beta.routines.disable +.beta.routines.dispatch +.beta.routines.enable +.beta.routines.get +.beta.routines.list +.beta.routines.list_runs + +.beta.schedules.create_or_update +.beta.schedules.delete +.beta.schedules.get +.beta.schedules.get_run +.beta.schedules.list +.beta.schedules.list_runs + +.beta.skills.create +.beta.skills.create_from_package +.beta.skills.delete +.beta.skills.download +.beta.skills.get +.beta.skills.list +.beta.skills.update + +.beta.toolboxes.create_version +.beta.toolboxes.delete +.beta.toolboxes.delete_version +.beta.toolboxes.get +.beta.toolboxes.get_version +.beta.toolboxes.list +.beta.toolboxes.list_versions +.beta.toolboxes.update +``` diff --git a/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd b/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd index ca22422a244f..594848cfb591 100644 --- a/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd +++ b/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd @@ -62,7 +62,17 @@ 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` (azure\ai\projects\_utils\utils.py). +REM The emitter generates the multipart-file loop before the data-field loop, so JSON +REM metadata parts end up after large binary file parts in the encoded body. Some +REM streaming server-side parsers (e.g. the Foundry hosted-agents +REM `create_agent_version_from_code` endpoint) require the small JSON metadata parts +REM to precede the binary file parts; otherwise they report the metadata part as missing. +REM This rewrite swaps the two loops so data fields are appended first. +powershell -Command "$f='azure\ai\projects\_utils\utils.py'; $c=Get-Content $f -Raw; if ($c -notmatch 'Append data fields first') { $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):',' files.extend([(multipart_field, e) for e in multipart_entry])',' elif multipart_entry:',' files.append((multipart_field, multipart_entry))','',' 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. diff --git a/sdk/ai/azure-ai-projects/pyrightconfig.json b/sdk/ai/azure-ai-projects/pyrightconfig.json index b8b684eb3af1..c4714f25326c 100644 --- a/sdk/ai/azure-ai-projects/pyrightconfig.json +++ b/sdk/ai/azure-ai-projects/pyrightconfig.json @@ -3,9 +3,13 @@ "reportMissingImports": false, "pythonVersion": "3.11", "extraPaths": [ + "./../../authorization/azure-mgmt-authorization", + "./../../cognitiveservices/azure-mgmt-cognitiveservices", "./../../core/azure-core", "./../../evaluation/azure-ai-evaluation", "./../../identity/azure-identity", + "./../../resources/azure-mgmt-resource", + "./../../resources/azure-mgmt-subscription", "./../../monitor/azure-monitor-opentelemetry" ] } \ No newline at end of file diff --git a/sdk/ai/azure-ai-projects/samples/agents/sample_agent_basic.py b/sdk/ai/azure-ai-projects/samples/agents/sample_agent_basic.py index 64b3161d4af6..260902b53c4b 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/sample_agent_basic.py +++ b/sdk/ai/azure-ai-projects/samples/agents/sample_agent_basic.py @@ -42,7 +42,6 @@ AIProjectClient(endpoint=endpoint, credential=credential) as project_client, ): - # [START prompt_agent_basic] with project_client.get_openai_client() as openai_client: agent = project_client.agents.create_version( agent_name="MyAgent", @@ -81,4 +80,3 @@ project_client.agents.delete_version(agent_name=agent.name, agent_version=agent.version) print("Agent deleted") - # [END prompt_agent_basic] diff --git a/sdk/ai/azure-ai-projects/samples/agents/sample_external_agents_crud.py b/sdk/ai/azure-ai-projects/samples/agents/sample_external_agents_crud.py new file mode 100644 index 000000000000..4cbb5907ac1c --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/agents/sample_external_agents_crud.py @@ -0,0 +1,79 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + This sample demonstrates how to perform CRUD operations on External Agents + using the synchronous AIProjectClient. + + External Agents are a preview feature. They register third-party agents hosted + outside Microsoft Foundry. Registration is metadata-only: Foundry uses the + OpenTelemetry agent identifier to light up traces and evaluations for spans + emitted by your external agent. + +USAGE: + python sample_external_agents_crud.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. +""" + +import os + +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 ( + ExternalAgentDefinition, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client, +): + agent_name = "sample-external-agent" + otel_agent_id = "sample-external-agent" + + try: + project_client.agents.delete(agent_name, force=True) + print(f"External agent `{agent_name}` deleted") + except ResourceNotFoundError: + pass + + created = project_client.agents.create_version( + agent_name=agent_name, + definition=ExternalAgentDefinition(otel_agent_id=otel_agent_id), + description="External agent registered by the azure-ai-projects sample.", + metadata={"sample": "external_agents_crud", "status": "created"}, + ) + print(f"Created external agent: {created.name} version={created.version} otel_agent_id={otel_agent_id}") + + fetched_agent = project_client.agents.get(agent_name) + print(f"Retrieved external agent: {fetched_agent.name} latest_version={fetched_agent.versions.latest.version}") + + fetched_version = project_client.agents.get_version(agent_name=agent_name, agent_version=created.version) + print(f"Retrieved external agent version: {fetched_version.name} version={fetched_version.version}") + + external_agents = list(project_client.agents.list(kind="external", limit=10)) + print(f"Found {len(external_agents)} external agents or more") + for external_agent in external_agents: + print(f" - {external_agent.name} ({external_agent.id})") + + deleted = project_client.agents.delete(agent_name, force=True) + print(f"Deleted external agent: {deleted.name} deleted={deleted.deleted}") diff --git a/sdk/ai/azure-ai-projects/samples/agents/sample_external_agents_crud_async.py b/sdk/ai/azure-ai-projects/samples/agents/sample_external_agents_crud_async.py new file mode 100644 index 000000000000..f876d077a56d --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/agents/sample_external_agents_crud_async.py @@ -0,0 +1,87 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + This sample demonstrates how to perform CRUD operations on External Agents + using the asynchronous AIProjectClient. + + External Agents are a preview feature. They register third-party agents hosted + outside Microsoft Foundry. Registration is metadata-only: Foundry uses the + OpenTelemetry agent identifier to light up traces and evaluations for spans + emitted by your external agent. + +USAGE: + python sample_external_agents_crud_async.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" python-dotenv aiohttp + + 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. +""" + +import asyncio +import os + +from dotenv import load_dotenv + +from azure.core.exceptions import ResourceNotFoundError +from azure.identity.aio import DefaultAzureCredential + +from azure.ai.projects.aio import AIProjectClient +from azure.ai.projects.models import ( + ExternalAgentDefinition, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + + +async def main() -> None: + async with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client, + ): + agent_name = "sample-external-agent" + otel_agent_id = "sample-external-agent" + + try: + await project_client.agents.delete(agent_name, force=True) + print(f"External agent `{agent_name}` deleted") + except ResourceNotFoundError: + pass + + created = await project_client.agents.create_version( + agent_name=agent_name, + definition=ExternalAgentDefinition(otel_agent_id=otel_agent_id), + description="External agent registered by the azure-ai-projects sample.", + metadata={"sample": "external_agents_crud", "status": "created"}, + ) + print(f"Created external agent: {created.name} version={created.version} otel_agent_id={otel_agent_id}") + + fetched_agent = await project_client.agents.get(agent_name) + print(f"Retrieved external agent: {fetched_agent.name} latest_version={fetched_agent.versions.latest.version}") + + fetched_version = await project_client.agents.get_version(agent_name=agent_name, agent_version=created.version) + print(f"Retrieved external agent version: {fetched_version.name} version={fetched_version.version}") + + external_agents = [] + async for external_agent in project_client.agents.list(kind="external", limit=10): + external_agents.append(external_agent) + print(f"Found {len(external_agents)} external agents or more") + for external_agent in external_agents: + print(f" - {external_agent.name} ({external_agent.id})") + + deleted = await project_client.agents.delete(agent_name, force=True) + print(f"Deleted external agent: {deleted.name} deleted={deleted.deleted}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_azure_monitor_tracing.py b/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_azure_monitor_tracing.py index a9cd0f9907b2..795d5aa7c6dc 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_azure_monitor_tracing.py +++ b/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_azure_monitor_tracing.py @@ -31,11 +31,9 @@ import os from dotenv import load_dotenv -# [START imports_for_azure_monitor_tracing] from opentelemetry import trace from azure.monitor.opentelemetry import configure_azure_monitor -# [END imports_for_azure_monitor_tracing] from azure.identity import DefaultAzureCredential from azure.ai.projects import AIProjectClient from azure.ai.projects.models import PromptAgentDefinition @@ -48,18 +46,14 @@ DefaultAzureCredential() as credential, AIProjectClient(endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], credential=credential) as project_client, ): - # [START setup_azure_monitor_tracing] # Enable Azure Monitor tracing application_insights_connection_string = project_client.telemetry.get_application_insights_connection_string() configure_azure_monitor(connection_string=application_insights_connection_string) - # [END setup_azure_monitor_tracing] - # [START create_span_for_scenario] tracer = trace.get_tracer(__name__) scenario = os.path.basename(__file__) with tracer.start_as_current_span(scenario): - # [END create_span_for_scenario] with project_client.get_openai_client() as openai_client: agent_definition = PromptAgentDefinition( model=os.environ["FOUNDRY_MODEL_NAME"], diff --git a/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_console_tracing.py b/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_console_tracing.py index 85e1504783d2..8b9be473e7dc 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_console_tracing.py +++ b/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_console_tracing.py @@ -31,7 +31,6 @@ from typing import Any from dotenv import load_dotenv -# [START imports_for_console_tracing] from azure.core.settings import settings settings.tracing_implementation = "opentelemetry" @@ -40,7 +39,6 @@ from opentelemetry.sdk.trace.export import SimpleSpanProcessor, ConsoleSpanExporter from azure.ai.projects.telemetry import AIProjectInstrumentor -# [END imports_for_console_tracing] from azure.identity import DefaultAzureCredential from azure.ai.projects import AIProjectClient @@ -72,7 +70,6 @@ def display_conversation_item(item: Any) -> None: # pylint: disable=redefined-o print("---") -# [START setup_console_tracing] # Setup tracing to console # Requires opentelemetry-sdk span_exporter = ConsoleSpanExporter() @@ -83,12 +80,9 @@ def display_conversation_item(item: Any) -> None: # pylint: disable=redefined-o # Enable instrumentation with content tracing AIProjectInstrumentor().instrument() -# [END setup_console_tracing] -# [START create_span_for_scenario] scenario = os.path.basename(__file__) with tracer.start_as_current_span(scenario): - # [END create_span_for_scenario] with ( DefaultAzureCredential() as credential, AIProjectClient(endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], credential=credential) as project_client, diff --git a/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_console_tracing_custom_attributes.py b/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_console_tracing_custom_attributes.py index 44251d2ae830..5bfcea4034e0 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_console_tracing_custom_attributes.py +++ b/sdk/ai/azure-ai-projects/samples/agents/telemetry/sample_agent_basic_with_console_tracing_custom_attributes.py @@ -50,7 +50,6 @@ # Define the custom span processor that is used for adding the custom # attributes to spans when they are started. -# [START custom_attribute_span_processor] class CustomAttributeSpanProcessor(SpanProcessor): def __init__(self) -> None: pass @@ -68,8 +67,6 @@ def on_end(self, span: ReadableSpan): pass -# [END custom_attribute_span_processor] - # Setup tracing to console # Requires opentelemetry-sdk span_exporter = ConsoleSpanExporter() @@ -82,10 +79,8 @@ def on_end(self, span: ReadableSpan): AIProjectInstrumentor().instrument() # Add the custom span processor to the global tracer provider -# [START add_custom_span_processor_to_tracer_provider] provider = cast(TracerProvider, trace.get_tracer_provider()) provider.add_span_processor(CustomAttributeSpanProcessor()) -# [END add_custom_span_processor_to_tracer_provider] scenario = os.path.basename(__file__) with tracer.start_as_current_span(scenario): diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_ai_search.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_ai_search.py index f2b9f01ee657..e5d6d816ea6e 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_ai_search.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_ai_search.py @@ -49,7 +49,6 @@ project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = AzureAISearchTool( azure_ai_search=AzureAISearchToolResource( indexes=[ @@ -61,7 +60,6 @@ ] ) ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_azure_function.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_azure_function.py index 5e78c32d6325..09c98f891a00 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_azure_function.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_azure_function.py @@ -52,7 +52,6 @@ project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = AzureFunctionTool( azure_function=AzureFunctionDefinition( input_binding=AzureFunctionBinding( @@ -77,7 +76,6 @@ ), ) ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_bing_custom_search.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_bing_custom_search.py index fd3a6a1910b3..0f52fbfa8e47 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_bing_custom_search.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_bing_custom_search.py @@ -58,7 +58,6 @@ project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = BingCustomSearchPreviewTool( bing_custom_search_preview=BingCustomSearchToolParameters( search_configurations=[ @@ -69,7 +68,6 @@ ] ) ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_bing_grounding.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_bing_grounding.py index 386d22dc5e45..735462eb82b0 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_bing_grounding.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_bing_grounding.py @@ -63,7 +63,6 @@ project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = BingGroundingTool( bing_grounding=BingGroundingSearchToolParameters( search_configurations=[ @@ -71,7 +70,6 @@ ] ) ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_browser_automation.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_browser_automation.py index 457e79f40388..54471e71aff4 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_browser_automation.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_browser_automation.py @@ -47,7 +47,6 @@ project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = BrowserAutomationPreviewTool( browser_automation_preview=BrowserAutomationToolParameters( connection=BrowserAutomationToolConnectionParameters( @@ -55,7 +54,6 @@ ) ) ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_code_interpreter.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_code_interpreter.py index 0f053b52a860..d1da6fcd4181 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_code_interpreter.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_code_interpreter.py @@ -39,9 +39,7 @@ project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = CodeInterpreterTool() - # [END tool_declaration] # Create agent with code interpreter tool agent = project_client.agents.create_version( @@ -69,11 +67,9 @@ print(f"Response completed (id: {response.id})") # Print code executed by the code interpreter tool. - # [START code_output_extraction] code = next((output.code for output in response.output if output.type == "code_interpreter_call"), "") print("Code Interpreter code:") print(code) - # [END code_output_extraction] # Print final assistant text output. print(f"Agent response: {response.output_text}") diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_code_interpreter_with_files.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_code_interpreter_with_files.py index 68942c42cda8..3421bd5715d9 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_code_interpreter_with_files.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_code_interpreter_with_files.py @@ -40,7 +40,6 @@ project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] # Load the CSV file to be processed asset_file_path = os.path.abspath( os.path.join(os.path.dirname(__file__), "../assets/synthetic_500_quarterly_results.csv") @@ -51,7 +50,6 @@ file = openai_client.files.create(purpose="assistants", file=f) tool = CodeInterpreterTool(container=AutoCodeInterpreterToolParam(file_ids=[file.id])) - # [END tool_declaration] print(f"File uploaded (id: {file.id})") diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_computer_use.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_computer_use.py index d77cc7dae39e..59e5af777531 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_computer_use.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_computer_use.py @@ -63,9 +63,7 @@ print("Failed to load required screenshot assets. Please ensure the asset files exist in ../assets/") exit(1) # pylint: disable=consider-using-sys-exit - # [START tool_declaration] tool = ComputerUsePreviewTool(display_width=1026, display_height=769, environment="windows") - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_fabric.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_fabric.py index 3ceb1ee1d28b..587394c4e1aa 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_fabric.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_fabric.py @@ -46,7 +46,6 @@ AIProjectClient(endpoint=endpoint, credential=credential) as project_client, project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = MicrosoftFabricPreviewTool( fabric_dataagent_preview=FabricDataAgentToolParameters( project_connections=[ @@ -54,7 +53,6 @@ ] ) ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_fabric_iq.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_fabric_iq.py new file mode 100644 index 000000000000..f43fba64a401 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_fabric_iq.py @@ -0,0 +1,69 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + This sample demonstrates how to run a Prompt Agent that uses the + Fabric IQ preview tool with a synchronous client. + +USAGE: + python sample_agent_fabric_iq.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_MODEL_NAME - The deployment name of the AI model, as found under the "Name" column in + the "Models + endpoints" tab in your Microsoft Foundry project. + 3) FABRIC_IQ_PROJECT_CONNECTION_ID - The fully-qualified resource id of the Fabric IQ project connection. + 4) FABRIC_IQ_USER_INPUT - The natural-language question to send to the agent. +""" + +import os +from dotenv import load_dotenv +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import PromptAgentDefinition, FabricIQPreviewTool + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as openai_client, +): + tool_payload = FabricIQPreviewTool( + project_connection_id=os.environ["FABRIC_IQ_PROJECT_CONNECTION_ID"], + require_approval="never", + ) + + agent = project_client.agents.create_version( + agent_name="MyAgent", + definition=PromptAgentDefinition( + model=os.environ["FOUNDRY_MODEL_NAME"], + instructions="Use the available Fabric IQ tools to answer questions and perform tasks.", + tools=[tool_payload], + ), + ) + print(f"Agent created (id: {agent.id}, name: {agent.name}, version: {agent.version})") + + user_input = os.environ.get("FABRIC_IQ_USER_INPUT") or input("Enter your question:\n") + + response = openai_client.responses.create( + input=user_input, + extra_body={"agent_reference": {"name": agent.name, "type": "agent_reference"}}, + ) + + print(f"Agent response: {response.output_text}") + + # Clean up the agent version so unused versions don't accumulate in the project. + project_client.agents.delete_version(agent_name=agent.name, agent_version=agent.version) + print("Agent deleted") diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_fabric_iq_async.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_fabric_iq_async.py new file mode 100644 index 000000000000..bdd5881cba07 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_fabric_iq_async.py @@ -0,0 +1,76 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + This sample demonstrates how to run a Prompt Agent that uses the + Fabric IQ preview tool with an asynchronous client. + +USAGE: + python sample_agent_fabric_iq_async.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" python-dotenv aiohttp + + 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_MODEL_NAME - The deployment name of the AI model, as found under the "Name" column in + the "Models + endpoints" tab in your Microsoft Foundry project. + 3) FABRIC_IQ_PROJECT_CONNECTION_ID - The fully-qualified resource id of the Fabric IQ project connection. + 4) FABRIC_IQ_USER_INPUT - The natural-language question to send to the agent. +""" + +import os +import asyncio +from dotenv import load_dotenv +from azure.identity.aio import DefaultAzureCredential +from azure.ai.projects.aio import AIProjectClient +from azure.ai.projects.models import PromptAgentDefinition, FabricIQPreviewTool + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + + +async def main(): + async with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as openai_client, + ): + tool_payload = FabricIQPreviewTool( + project_connection_id=os.environ["FABRIC_IQ_PROJECT_CONNECTION_ID"], + require_approval="never", + ) + + agent = await project_client.agents.create_version( + agent_name="MyAgent", + definition=PromptAgentDefinition( + model=os.environ["FOUNDRY_MODEL_NAME"], + instructions="Use the available Fabric IQ tools to answer questions and perform tasks.", + tools=[tool_payload], + ), + ) + print(f"Agent created (id: {agent.id}, name: {agent.name}, version: {agent.version})") + + user_input = os.environ.get("FABRIC_IQ_USER_INPUT") or input("Enter your question:\n") + + response = await openai_client.responses.create( + input=user_input, + extra_body={"agent_reference": {"name": agent.name, "type": "agent_reference"}}, + ) + + print(f"Agent response: {response.output_text}") + + # Clean up the agent version so unused versions don't accumulate in the project. + await project_client.agents.delete_version(agent_name=agent.name, agent_version=agent.version) + print("Agent deleted") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_file_search.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_file_search.py index 2a15ce981d7b..e6f64c060be0 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_file_search.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_file_search.py @@ -39,7 +39,6 @@ AIProjectClient(endpoint=endpoint, credential=credential) as project_client, project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] # Create vector store for file search vector_store = openai_client.vector_stores.create(name="ProductInfoStore") print(f"Vector store created (id: {vector_store.id})") @@ -53,7 +52,6 @@ print(f"File uploaded to vector store (id: {file.id})") tool = FileSearchTool(vector_store_ids=[vector_store.id]) - # [END tool_declaration] # Create agent with file search tool agent = project_client.agents.create_version( diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_function_tool.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_function_tool.py index 7e0ac8e0e56e..991406256f1a 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_function_tool.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_function_tool.py @@ -48,7 +48,6 @@ def get_horoscope(sign: str) -> str: project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = FunctionTool( name="get_horoscope", parameters={ @@ -65,7 +64,6 @@ def get_horoscope(sign: str) -> str: description="Get today's horoscope for an astrological sign.", strict=True, ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_image_generation.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_image_generation.py index 5232a443ca34..b1a4e20fda11 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_image_generation.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_image_generation.py @@ -62,13 +62,11 @@ ): image_generation_model = os.environ["IMAGE_GENERATION_MODEL_DEPLOYMENT_NAME"] - # [START tool_declaration] tool = ImageGenTool( model=image_generation_model, # Model such as "gpt-image-1" quality="low", size="1024x1024", ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", @@ -94,7 +92,6 @@ project_client.agents.delete_version(agent_name=agent.name, agent_version=agent.version) print("Agent deleted") - # [START download_image] image_data = [output.result for output in response.output if output.type == "image_generation_call"] if image_data and image_data[0]: print("Downloading generated image...") @@ -104,5 +101,4 @@ with open(file_path, "wb") as f: f.write(base64.b64decode(image_data[0])) - # [END download_image] print(f"Image saved to: {file_path}") diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_mcp.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_mcp.py index fb0a553328e9..934a83781e92 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_mcp.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_mcp.py @@ -39,13 +39,11 @@ AIProjectClient(endpoint=endpoint, credential=credential) as project_client, project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] mcp_tool = MCPTool( server_label="api-specs", server_url="https://gitmcp.io/Azure/azure-rest-api-specs", require_approval="always", ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_mcp_with_project_connection.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_mcp_with_project_connection.py index 091db4707b59..3042ac9458db 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_mcp_with_project_connection.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_mcp_with_project_connection.py @@ -42,14 +42,12 @@ project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = MCPTool( server_label="api-specs", server_url="https://api.githubcopilot.com/mcp", require_approval="always", project_connection_id=os.environ["MCP_PROJECT_CONNECTION_ID"], ) - # [END tool_declaration] # Create a prompt agent with MCP tool capabilities agent = project_client.agents.create_version( diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_memory_search.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_memory_search.py index 21bce947edc8..433b315b2920 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_memory_search.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_memory_search.py @@ -75,7 +75,6 @@ ) print(f"Created memory store: {memory_store.name} ({memory_store.id}): {memory_store.description}") - # [START memory_search_tool_declaration] # Set scope to associate the memories with # You can also use "{{$userId}}" to take the oid of the request authentication header scope = "user_123" @@ -86,7 +85,6 @@ update_delay=1, # Wait 1 second of inactivity before updating memories # In a real application, set this to a higher value like 300 (5 minutes, default) ) - # [END memory_search_tool_declaration] # Create a prompt agent with memory search tool agent = project_client.agents.create_version( diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_openapi.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_openapi.py index 804c330cd0ce..729971029a6d 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_openapi.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_openapi.py @@ -48,7 +48,6 @@ weather_asset_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../assets/weather_openapi.json")) - # [START tool_declaration] with open(weather_asset_file_path, "r", encoding="utf-8") as f: openapi_weather = cast(dict[str, Any], jsonref.loads(f.read())) @@ -60,7 +59,6 @@ auth=OpenApiAnonymousAuthDetails(), ) ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_openapi_with_project_connection.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_openapi_with_project_connection.py index 6c7d88ba4ba4..890b7388608a 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_openapi_with_project_connection.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_openapi_with_project_connection.py @@ -54,7 +54,6 @@ os.path.join(os.path.dirname(__file__), "../assets/tripadvisor_openapi.json") ) - # [START tool_declaration] with open(tripadvisor_asset_file_path, "r", encoding="utf-8") as f: openapi_tripadvisor = cast(dict[str, Any], jsonref.loads(f.read())) @@ -70,7 +69,6 @@ ), ) ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_sharepoint.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_sharepoint.py index 394e486cc36a..d1c4db82eb1c 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_sharepoint.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_sharepoint.py @@ -46,7 +46,6 @@ AIProjectClient(endpoint=endpoint, credential=credential) as project_client, project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = SharepointPreviewTool( sharepoint_grounding_preview=SharepointGroundingToolParameters( project_connections=[ @@ -54,7 +53,6 @@ ] ) ) - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_to_agent.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_to_agent.py index 3fe3835b093a..edb7a46fc760 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_to_agent.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_to_agent.py @@ -49,14 +49,12 @@ project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = A2APreviewTool( project_connection_id=os.environ["A2A_PROJECT_CONNECTION_ID"], ) # If the connection is missing target, we need to set the A2A endpoint URL. if os.environ.get("A2A_ENDPOINT"): tool.base_url = os.environ["A2A_ENDPOINT"] - # [END tool_declaration] agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search.py index 3d21a92ae1b6..a08477e27096 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search.py @@ -53,9 +53,7 @@ AIProjectClient(endpoint=endpoint, credential=credential) as project_client, project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = WebSearchTool(user_location=WebSearchApproximateLocation(country="GB", city="London", region="London")) - # [END tool_declaration] # Create Agent with web search tool agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search_preview.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search_preview.py index cf68ceaf09cf..cdef1789b143 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search_preview.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search_preview.py @@ -49,9 +49,7 @@ AIProjectClient(endpoint=endpoint, credential=credential) as project_client, project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = WebSearchPreviewTool(user_location=ApproximateLocation(country="GB", city="London", region="London")) - # [END tool_declaration] # Create Agent with web search tool agent = project_client.agents.create_version( agent_name="MyAgent105", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search_with_custom_search.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search_with_custom_search.py index e4ad578919b5..b66e18e00241 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search_with_custom_search.py +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_search_with_custom_search.py @@ -58,14 +58,12 @@ AIProjectClient(endpoint=endpoint, credential=credential) as project_client, project_client.get_openai_client() as openai_client, ): - # [START tool_declaration] tool = WebSearchTool( custom_search_configuration=WebSearchConfiguration( project_connection_id=os.environ["BING_CUSTOM_SEARCH_PROJECT_CONNECTION_ID"], instance_name=os.environ["BING_CUSTOM_SEARCH_INSTANCE_NAME"], ) ) - # [END tool_declaration] # Create Agent with web search tool agent = project_client.agents.create_version( agent_name="MyAgent", diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_toolboxes_with_search_preview.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_toolboxes_with_search_preview.py new file mode 100644 index 000000000000..63bdc57e0ee8 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_toolboxes_with_search_preview.py @@ -0,0 +1,125 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + This sample demonstrates how to create a Toolbox in tool-search mode and + invoke it from a Prompt Agent using the synchronous AIProjectClient and + the OpenAI-compatible client. + + A toolbox version that includes 'ToolboxSearchPreviewTool' exposes only + two meta tools at its '/mcp' endpoint -- 'tool_search' and 'call_tool' + -- and defers every other tool behind them. The agent uses an 'MCPTool' + pointed at the toolbox's versioned '/mcp' URL to discover and invoke + those inner tools. + + Toolboxes and tool search are preview features. CRUD goes through + 'project_client.beta.toolboxes'. + +USAGE: + python sample_toolboxes_with_search_preview.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" python-dotenv openai + + 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_MODEL_NAME - The deployment name of the AI model, as found under the "Name" column in + the "Models + endpoints" tab in your Microsoft Foundry project. + 3) MCP_PROJECT_CONNECTION_ID - The connection resource ID in Custom keys used by + the inner MCP server inside the toolbox. +""" + +import os + +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 ( + MCPTool, + PromptAgentDefinition, + ToolboxSearchPreviewTool, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + +TOOLBOX_NAME = "toolbox_with_mcp_tool" +INNER_MCP_LABEL = "github" +INNER_MCP_URL = "https://api.githubcopilot.com/mcp" +TOOLBOX_MCP_LABEL = "search-tool" + + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as openai_client, +): + + inner_mcp_tool = MCPTool( + server_label=INNER_MCP_LABEL, + server_url=INNER_MCP_URL, + require_approval="never", + project_connection_id=os.environ["MCP_PROJECT_CONNECTION_ID"], + ) + + toolbox_version = project_client.beta.toolboxes.create_version( + name=TOOLBOX_NAME, + description=f"Toolbox with `{INNER_MCP_LABEL}` MCP server and tool search enabled.", + tools=[inner_mcp_tool, ToolboxSearchPreviewTool()], + ) + print(f"Created toolbox `{TOOLBOX_NAME}` (version {toolbox_version.version}).") + + toolbox_mcp_url = f"{endpoint}/toolboxes/{TOOLBOX_NAME}/versions/{toolbox_version.version}/mcp?api-version=v1" + token = credential.get_token("https://ai.azure.com/.default").token + + toolbox_mcp_tool = MCPTool( + server_label=TOOLBOX_MCP_LABEL, + server_url=toolbox_mcp_url, + authorization=token, + headers={"Foundry-Features": "Toolboxes=V1Preview"}, + require_approval="never", + ) + + agent = project_client.agents.create_version( + agent_name="MyAgent", + definition=PromptAgentDefinition( + model=os.environ["FOUNDRY_MODEL_NAME"], + instructions=( + "Always use the toolbox search tool to answer questions and perform tasks. " + "Use `tool_search` to discover a relevant tool, then `call_tool` " + "with the tool name returned by the search." + ), + tools=[toolbox_mcp_tool], + ), + ) + print(f"Agent created (id: {agent.id}, name: {agent.name}, version: {agent.version}).") + + response = openai_client.responses.create( + input="What is my username in Github profile?", + extra_body={"agent_reference": {"name": agent.name, "type": "agent_reference"}}, + ) + + for item in response.output: + if item.type == "mcp_approval_request": + print(f"server_label={item.server_label}, name={item.name}") + elif item.type == "mcp_list_tools": + print(f"server_label={item.server_label}, tools={[t.name for t in (item.tools or [])]}") + elif item.type == "mcp_call": + print(f"server_label={item.server_label}, name={item.name}, error={item.error}") + else: + print() + + print(f"Response: {response.output_text}") + + project_client.agents.delete_version(agent_name=agent.name, agent_version=agent.version) + print(f"Agent version {agent.version} deleted.") diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_toolboxes_with_search_preview_async.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_toolboxes_with_search_preview_async.py new file mode 100644 index 000000000000..bfd43071c2f3 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_toolboxes_with_search_preview_async.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 Toolbox in tool-search mode and + invoke it from a Prompt Agent using the asynchronous AIProjectClient and + the OpenAI-compatible client. + + A toolbox version that includes 'ToolboxSearchPreviewTool' exposes only + two meta tools at its '/mcp' endpoint -- 'tool_search' and 'call_tool' + -- and defers every other tool behind them. The agent uses an 'MCPTool' + pointed at the toolbox's versioned '/mcp' URL to discover and invoke + those inner tools. + + Toolboxes and tool search are preview features. CRUD goes through + 'project_client.beta.toolboxes'. + +USAGE: + python sample_toolboxes_with_search_preview_async.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" python-dotenv openai aiohttp + + 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_MODEL_NAME - The deployment name of the AI model, as found under the "Name" column in + the "Models + endpoints" tab in your Microsoft Foundry project. + 3) MCP_PROJECT_CONNECTION_ID - The connection resource ID in Custom keys used by + the inner MCP server inside the toolbox. +""" + +import asyncio +import os + +from dotenv import load_dotenv + +from azure.identity.aio import DefaultAzureCredential + +from azure.ai.projects.aio import AIProjectClient +from azure.ai.projects.models import ( + MCPTool, + PromptAgentDefinition, + ToolboxSearchPreviewTool, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + +TOOLBOX_NAME = "toolbox_with_mcp_tool" +INNER_MCP_LABEL = "github" +INNER_MCP_URL = "https://api.githubcopilot.com/mcp" +TOOLBOX_MCP_LABEL = "search-tool" + + +async def main() -> None: + async with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as openai_client, + ): + + inner_mcp_tool = MCPTool( + server_label=INNER_MCP_LABEL, + server_url=INNER_MCP_URL, + require_approval="never", + project_connection_id=os.environ["MCP_PROJECT_CONNECTION_ID"], + ) + + toolbox_version = await project_client.beta.toolboxes.create_version( + name=TOOLBOX_NAME, + description=f"Toolbox with `{INNER_MCP_LABEL}` MCP server and tool search enabled.", + tools=[inner_mcp_tool, ToolboxSearchPreviewTool()], + ) + print(f"Created toolbox `{TOOLBOX_NAME}` (version {toolbox_version.version}).") + + toolbox_mcp_url = f"{endpoint}/toolboxes/{TOOLBOX_NAME}/versions/{toolbox_version.version}/mcp?api-version=v1" + token = (await credential.get_token("https://ai.azure.com/.default")).token + + toolbox_mcp_tool = MCPTool( + server_label=TOOLBOX_MCP_LABEL, + server_url=toolbox_mcp_url, + authorization=token, + headers={"Foundry-Features": "Toolboxes=V1Preview"}, + require_approval="never", + ) + + agent = await project_client.agents.create_version( + agent_name="MyAgent", + definition=PromptAgentDefinition( + model=os.environ["FOUNDRY_MODEL_NAME"], + instructions=( + "Always use the toolbox search tool to answer questions and perform tasks. " + "Use `tool_search` to discover a relevant tool, then `call_tool` " + "with the tool name returned by the search." + ), + tools=[toolbox_mcp_tool], + ), + ) + print(f"Agent created (id: {agent.id}, name: {agent.name}, version: {agent.version}).") + + response = await openai_client.responses.create( + input="What is my username in Github profile?", + extra_body={"agent_reference": {"name": agent.name, "type": "agent_reference"}}, + ) + + for item in response.output: + if item.type == "mcp_approval_request": + print(f"server_label={item.server_label}, name={item.name}") + elif item.type == "mcp_list_tools": + print(f"server_label={item.server_label}, tools={[t.name for t in (item.tools or [])]}") + elif item.type == "mcp_call": + print(f"server_label={item.server_label}, name={item.name}, error={item.error}") + else: + print() + + print(f"Response: {response.output_text}") + + await project_client.agents.delete_version(agent_name=agent.name, agent_version=agent.version) + print(f"Agent version {agent.version} deleted.") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/sdk/ai/azure-ai-projects/samples/chat_completions/sample_chat_completions_basic.py b/sdk/ai/azure-ai-projects/samples/chat_completions/sample_chat_completions_basic.py index 78d07ed5540e..845dcd015d58 100644 --- a/sdk/ai/azure-ai-projects/samples/chat_completions/sample_chat_completions_basic.py +++ b/sdk/ai/azure-ai-projects/samples/chat_completions/sample_chat_completions_basic.py @@ -41,7 +41,6 @@ DefaultAzureCredential() as credential, AIProjectClient(endpoint=endpoint, credential=credential) as project_client, ): - # [START chat_completions] with project_client.get_openai_client() as openai_client: messages: list[ChatCompletionMessageParam] = [ {"role": "system", "content": "You are a helpful assistant."}, @@ -53,7 +52,6 @@ messages=messages, temperature=0, ) - # [END chat_completions] assistant_message = completion.choices[0].message.content print(f"Assistant: {assistant_message}") diff --git a/sdk/ai/azure-ai-projects/samples/connections/sample_connections.py b/sdk/ai/azure-ai-projects/samples/connections/sample_connections.py index 1de80cfdd689..c9fcbf78bc8c 100644 --- a/sdk/ai/azure-ai-projects/samples/connections/sample_connections.py +++ b/sdk/ai/azure-ai-projects/samples/connections/sample_connections.py @@ -38,7 +38,6 @@ DefaultAzureCredential() as credential, AIProjectClient(endpoint=endpoint, credential=credential) as project_client, ): - # [START connections_sample] print("List all connections:") for connection in project_client.connections.list(): print(connection) @@ -66,4 +65,3 @@ print(f"Get the connection named `{connection_name}`, with its credentials:") connection = project_client.connections.get(connection_name, include_credentials=True) print(connection) - # [END connection_sample] diff --git a/sdk/ai/azure-ai-projects/samples/datasets/README.md b/sdk/ai/azure-ai-projects/samples/datasets/README.md new file mode 100644 index 000000000000..68a7e111ff82 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/datasets/README.md @@ -0,0 +1,42 @@ +# Azure AI Projects - Dataset Samples + +## Prerequisites + +Before running any sample: + +```bash +pip install "azure-ai-projects>=2.2.0" azure-identity python-dotenv +``` + +To run asynchronous samples, also install `aiohttp`. Samples that produce or consume Azure OpenAI files also require `openai`. + +Set these environment variables: + +| Variable | Required by | Value | +|---|---|---| +| `FOUNDRY_PROJECT_ENDPOINT` | All samples | Your Azure AI Project endpoint, e.g. `https://.services.ai.azure.com/api/projects/` | +| `FOUNDRY_MODEL_NAME` | `simple_qna` data-generation samples | An Azure OpenAI model deployment in your project. For **evaluation** jobs use a [Responses API](https://learn.microsoft.com/azure/foundry/openai/how-to/responses?tabs=python-key#model-support) model; for **fine-tuning** jobs use a chat-completions model (e.g. `gpt-4o`, `gpt-4.1`). | +| `FOUNDRY_AGENT_NAME` | Traces data-generation samples | An agent with recent traces in Application Insights. Foundry Agents and OpenTelemetry-instrumented third-party agents are both supported. | + +Optional per-sample variables (`DATASET_NAME`, `POLL_INTERVAL_SECONDS`, `FOUNDRY_TRACES_WINDOW_DAYS`, etc.) are documented in each sample's docstring. + +## Sample Index + +### Dataset Basics + +| Sample | Description | +|--------|-------------| +| [sample_datasets.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/datasets/sample_datasets.py) | Upload files, create, list, and delete versioned Datasets | +| [sample_datasets_async.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/datasets/sample_datasets_async.py) | Async version of the dataset CRUD sample | +| [sample_datasets_download.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/datasets/sample_datasets_download.py) | Upload a folder as a Dataset and download its files via an Azure storage ContainerClient | + +### Data Generation Jobs + +| Sample | Source(s) | Scenario | Description | +|--------|-----------|----------|-------------| +| [sample_dataset_generation_job_simpleqna_with_prompt_source.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_prompt_source.py) | Prompt | Evaluation | Generate a QnA dataset from an inline prompt and run an evaluation against it end-to-end | +| [sample_dataset_generation_job_simpleqna_with_file_source.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_file_source.py) | File (Azure OpenAI) + Prompt | Evaluation | Generate a QnA dataset from an Azure OpenAI File combined with an inline Prompt | +| [sample_dataset_generation_job_simpleqna_with_agent_source.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_agent_source.py) | Agent definition | Evaluation | Generate a QnA dataset by creating a prompt agent and sourcing the job from the agent's instructions | +| [sample_dataset_generation_job_traces_for_evaluation.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_traces_for_evaluation.py) | Traces | Evaluation | Generate a QnA dataset from an agent's recent conversation traces | +| [sample_dataset_generation_job_simpleqna_for_finetuning.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_for_finetuning.py) | File (Azure OpenAI) | Supervised fine-tuning | Generate supervised fine-tuning JSONL files (training and validation partitions) from an uploaded Azure OpenAI File | +| [sample_dataset_generation_job_traces_for_finetuning.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_traces_for_finetuning.py) | Traces | Supervised fine-tuning | Generate supervised fine-tuning JSONL files (training and validation partitions) from an agent's recent conversation traces | diff --git a/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_for_finetuning.py b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_for_finetuning.py new file mode 100644 index 000000000000..8a545c3dd6b3 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_for_finetuning.py @@ -0,0 +1,226 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Generates supervised fine-tuning data from a Markdown reference document + uploaded as an Azure OpenAI File. The sample: + + 1. Uploads a short reference document via the Azure OpenAI Files API + (`purpose=user_data`) so it can be referenced by file id. + 2. Creates a `DataGenerationJob` (scenario=SUPERVISED_FINETUNING, + type=simple_qna) that synthesizes short-answer and long-answer + question / answer pairs from the file content and emits them as + training and validation JSONL files. + 3. Polls the job to completion and prints every generated file output. + 4. Cleans up the generated fine-tuning files, the Azure OpenAI input file, and the data generation job. + + `simple_qna` REQUIRES `model_options` — the service uses the configured LLM + to synthesize the QnA pairs. Setting `train_split` triggers a split of + the generated samples into two Azure OpenAI output files. + +USAGE: + python sample_dataset_generation_job_simpleqna_for_finetuning.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity openai python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found + in the overview page of your Microsoft Foundry project. + 2) FOUNDRY_MODEL_NAME - Required. The name of an Azure OpenAI model + deployment used to synthesize the QnA samples. For `simple_qna` fine-tuning, + the deployment must support the chat completions API (e.g. `gpt-4o`, `gpt-4.1`). + 3) DATASET_NAME - Optional. Name to assign to the generated output files + (used as the file name prefix). Defaults to `simpleqna-finetuning-sample`. + The service caps the rendered output name at 50 characters, so keep + custom values short — the sample appends a unique run id suffix. + 4) POLL_INTERVAL_SECONDS - Optional. Number of seconds to sleep between status + polls for the data generation job. Defaults to 10. +""" + +import io +import os +import time +import uuid +from datetime import datetime, timezone + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import ( + DataGenerationJob, + DataGenerationJobInputs, + DataGenerationJobOutputOptions, + DataGenerationJobScenario, + DataGenerationModelOptions, + FileDataGenerationJobOutput, + FileDataGenerationJobSource, + JobStatus, + SimpleQnADataGenerationJobOptions, + SimpleQnAFineTuningQuestionType, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_name = os.environ["FOUNDRY_MODEL_NAME"] +dataset_name = os.environ.get("DATASET_NAME", "simpleqna-finetuning-sample") +poll_interval_seconds = int(os.environ.get("POLL_INTERVAL_SECONDS", "10")) + +# Unique per-run output name so repeated runs do not collide. +# Output names are capped at 50 characters by the service. +run_id = f"{datetime.now(tz=timezone.utc).strftime('%y%m%d%H%M%S')}-{uuid.uuid4().hex[:4]}" +output_name = f"{dataset_name}-{run_id}" +if len(output_name) > 50: + raise ValueError( + f"Output name `{output_name}` exceeds the 50-character service limit. " + f"Lower DATASET_NAME (currently `{dataset_name}`) so that `-` fits within 50 characters." + ) + +# Reference document the sample uploads as an Azure OpenAI file. The service +# requires the file to contain at least 1 KB of content to generate QnA from. +SEED_REFERENCE_DOCUMENT = """# Widgets and Gizmos Reference + +## Products +- Widget: blue, manufactured at Factory 7 in Acme, carbon-fiber, rated to 80 C, sold in packs of 4, 250 g each. +- Gizmo: red, manufactured at Factory 12 in Bedrock, carbon-fiber, rated to 80 C, sold individually, 1.2 kg each. +- Sprocket: green, manufactured at Factory 3 in Acme, stainless steel, rated to 200 C, sold individually, 500 g each. + +## Operations +- Factory operates weekdays 0700-1900 local time. +- Closed on public holidays, except for the annual maintenance run on December 27. +- ISO 9001 certified; audited annually by an independent third party. +- Quality control samples every 100th unit and runs full destructive testing on every 5000th unit. + +## Customer support +- Warranty claims: email support@example.com with the serial number printed on the underside of the product. +- Returns: accepted within 30 days if unopened; opened items are eligible for repair only. +- Bulk orders (50+ units): contact sales@example.com for volume pricing and an extended 90-day return window. +- Replacement parts: orderable directly from the support portal using the original order number. + +## Pricing and SLAs +- Widget pack: USD 24.99 per 4-pack; free shipping on orders over USD 75. +- Gizmo unit: USD 49.99; free shipping on orders over USD 75. +- Sprocket unit: USD 14.99; ships from regional warehouses in 1-2 business days. +- Standard support response: within one business day. Priority support response: within four hours. +""" + +TERMINAL_STATUSES = {JobStatus.SUCCEEDED, JobStatus.FAILED, JobStatus.CANCELLED} + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as openai_client, +): + + # ------------------------------------------------------------------ + # 1. Upload the seed reference document as an Azure OpenAI file. + # ------------------------------------------------------------------ + seed_filename = f"widgets-gizmos-seed-{run_id}.md" + print(f"Upload the seed reference document as Azure OpenAI file `{seed_filename}`.") + seed_file = openai_client.files.create( + file=(seed_filename, io.BytesIO(SEED_REFERENCE_DOCUMENT.encode("utf-8"))), + purpose="user_data", + ) + print(f"Uploaded Azure OpenAI file (id: {seed_file.id}).") + + # Wait for the file to finish processing — the data generation service + # rejects references to files that are not yet in the `processed` state. + print("Wait for the Azure OpenAI file to be processed.", end="", flush=True) + while seed_file.status not in ("processed", "error"): + time.sleep(2) + seed_file = openai_client.files.retrieve(file_id=seed_file.id) + print(".", end="", flush=True) + print() + if seed_file.status != "processed": + raise RuntimeError(f"Azure OpenAI file `{seed_file.id}` failed to process: status=`{seed_file.status}`.") + + # ------------------------------------------------------------------ + # 2. Submit a fine-tuning data generation job that consumes the file. + # ------------------------------------------------------------------ + print("Create a fine-tuning data generation job from the Azure OpenAI file.") + job = DataGenerationJob( + inputs=DataGenerationJobInputs( + name=f"simpleqna-finetuning-{run_id}", + scenario=DataGenerationJobScenario.SUPERVISED_FINETUNING, + sources=[ + FileDataGenerationJobSource( + description="Widgets & Gizmos product / operations reference (Azure OpenAI file).", + id=seed_file.id, + ), + ], + options=SimpleQnADataGenerationJobOptions( + # Service requires max_samples to be between 15 and 1000. + max_samples=15, + # `simple_qna` REQUIRES model_options. + model_options=DataGenerationModelOptions(model=model_name), + # Split generated samples 80% training / 20% validation. + train_split=0.8, + # Ask for both short-answer and long-answer questions. + question_types=[ + SimpleQnAFineTuningQuestionType.SHORT_ANSWER, + SimpleQnAFineTuningQuestionType.LONG_ANSWER, + ], + ), + output_options=DataGenerationJobOutputOptions(name=output_name), + ), + ) + job = project_client.beta.datasets.create_generation_job(job=job) + print(f"Created data generation job `{job.id}` (status: `{job.status}`).") + + print(f"Poll job `{job.id}` until it reaches a terminal state.", end="", flush=True) + while True: + job = project_client.beta.datasets.get_generation_job(job_id=job.id) + if job.status in TERMINAL_STATUSES: + break + time.sleep(poll_interval_seconds) + print(".", end="", flush=True) + print() + print(f"Final job status: `{job.status}`.") + + if job.status != JobStatus.SUCCEEDED: + message = job.error.message if job.error is not None else "" + raise RuntimeError(f"Job `{job.id}` ended with status `{job.status}`: {message}") + + # ------------------------------------------------------------------ + # 3. Inspect the generated fine-tuning file outputs. + # ------------------------------------------------------------------ + # `train_split=0.8` produces two Azure OpenAI files: a training partition + # and a validation partition. Both are emitted as FileDataGenerationJobOutput + # entries in `job.result.outputs`. + file_outputs = [ + output + for output in ((job.result.outputs if job.result is not None else None) or []) + if isinstance(output, FileDataGenerationJobOutput) + ] + if not file_outputs: + raise RuntimeError(f"Job `{job.id}` did not produce any file outputs.") + + print(f"Generated {len(file_outputs)} fine-tuning file(s):") + for output in file_outputs: + if not output.id: + raise RuntimeError(f"Job `{job.id}` returned a file output without an id.") + # Resolve the Azure OpenAI file to surface its real filename and size. + file_info = openai_client.files.retrieve(file_id=output.id) + print(f" - filename=`{file_info.filename}` id=`{output.id}` bytes={file_info.bytes}") + if job.result is not None and job.result.generated_samples is not None: + print(f"Generated samples: {job.result.generated_samples}") + + # ------------------------------------------------------------------ + # 4. Clean up. + # ------------------------------------------------------------------ + for output in file_outputs: + print(f"Delete the generated Azure OpenAI file `{output.id}`.") + openai_client.files.delete(file_id=output.id) + + print(f"Delete the Azure OpenAI input file `{seed_file.id}`.") + openai_client.files.delete(file_id=seed_file.id) + + print(f"Delete the data generation job `{job.id}`.") + project_client.beta.datasets.delete_generation_job(job_id=job.id) diff --git a/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_agent_source.py b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_agent_source.py new file mode 100644 index 000000000000..70063d32da26 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_agent_source.py @@ -0,0 +1,209 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Generates an evaluation dataset from a prompt agent's definition using the + `simple_qna` recipe. The sample is fully self-contained — it creates a + short-lived `PromptAgentDefinition` whose `instructions` give the service + enough material to synthesize QnA pairs, then uses that agent as the + source for the data generation job. The sample: + + 1. Creates a `PromptAgentDefinition` agent with domain-specific + instructions (a small Widgets & Gizmos customer-support persona). + 2. Creates a `DataGenerationJob` (scenario=EVALUATION, type=simple_qna) + whose source is an `Agent` reference pointing at the new agent. The + service fetches the agent's instructions / prompt and uses the + configured LLM to synthesize question / answer pairs from them. + 3. Polls the job to completion and resolves the resulting `DatasetVersion`. + 4. Cleans up the data generation job, the generated dataset, and the agent version. + + `simple_qna` REQUIRES `model_options`. For `simple_qna` evaluation jobs the + deployed model must support the Azure OpenAI Responses API. See the + supported-model list: + https://learn.microsoft.com/azure/foundry/openai/how-to/responses?tabs=python-key#model-support + + If you want to synthesize QnA from an agent's recorded conversation traces + instead of its definition, see + `sample_dataset_generation_job_traces_for_evaluation.py` (traces recipe). + +USAGE: + python sample_dataset_generation_job_simpleqna_with_agent_source.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found + in the overview page of your Microsoft Foundry project. + 2) FOUNDRY_MODEL_NAME - Required. The name of an Azure OpenAI model + deployment used both as the prompt agent's backing model and to synthesize + the QnA samples. For `simple_qna` evaluation, choose a Responses-API + capable model (see the link in the description). + 3) DATASET_NAME - Optional. Name to assign to the generated output dataset. + Defaults to `simpleqna-agent-source-sample`. The service caps the + rendered output name at 50 characters, so keep custom values short — + the sample appends a unique run id suffix. + 4) POLL_INTERVAL_SECONDS - Optional. Number of seconds to sleep between status + polls for the data generation job. Defaults to 10. +""" + +import os +import time +import uuid +from datetime import datetime, timezone + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import ( + AgentDataGenerationJobSource, + DataGenerationJob, + DataGenerationJobInputs, + DataGenerationJobOutputOptions, + DataGenerationJobScenario, + DataGenerationModelOptions, + DatasetDataGenerationJobOutput, + DatasetVersion, + JobStatus, + PromptAgentDefinition, + SimpleQnADataGenerationJobOptions, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_name = os.environ["FOUNDRY_MODEL_NAME"] +dataset_name = os.environ.get("DATASET_NAME", "simpleqna-agent-source-sample") +poll_interval_seconds = int(os.environ.get("POLL_INTERVAL_SECONDS", "10")) + +# Unique per-run names so repeated runs do not collide. +# Output names are capped at 50 characters by the service. +run_id = f"{datetime.now(tz=timezone.utc).strftime('%y%m%d%H%M%S')}-{uuid.uuid4().hex[:4]}" +output_dataset_name = f"{dataset_name}-{run_id}" +if len(output_dataset_name) > 50: + raise ValueError( + f"Output dataset name `{output_dataset_name}` exceeds the 50-character service limit. " + f"Lower DATASET_NAME (currently `{dataset_name}`) so that `-` fits within 50 characters." + ) + +# The prompt agent's instructions seed the QnA generation. Make them concrete +# and domain-specific so the service has enough material to synthesize from. +AGENT_INSTRUCTIONS = """You are a customer support assistant for Acme's "Widgets & Gizmos" product line. + +Product catalog you can answer about: +- Widget: blue, manufactured at Factory 7 in Acme, carbon-fiber, rated to 80 C, sold in packs of 4, 250 g each, USD 24.99 per pack. +- Gizmo: red, manufactured at Factory 12 in Bedrock, carbon-fiber, rated to 80 C, sold individually, 1.2 kg each, USD 49.99. +- Sprocket: green, manufactured at Factory 3 in Acme, stainless steel, rated to 200 C, sold individually, 500 g each, USD 14.99. + +Operational policy: +- Factory operates weekdays 0700-1900 local time and is closed on public holidays except the annual maintenance run on December 27. +- Warranty claims must include the serial number printed on the underside of the product, emailed to support@example.com. +- Returns are accepted within 30 days if unopened; opened items are eligible for repair only. +- Bulk orders of 50 or more units use the sales@example.com channel and get an extended 90-day return window. +- Standard support responds within one business day; priority support responds within four hours. +- Free shipping on orders over USD 75. + +When asked about anything outside this catalog and policy, politely say you do not have that information. +""" + +agent_name = f"widgets-gizmos-support-{run_id}" + +TERMINAL_STATUSES = {JobStatus.SUCCEEDED, JobStatus.FAILED, JobStatus.CANCELLED} + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, +): + + # ------------------------------------------------------------------ + # 1. Create a short-lived prompt agent to act as the source. + # ------------------------------------------------------------------ + print(f"Create prompt agent `{agent_name}`.") + agent = project_client.agents.create_version( + agent_name=agent_name, + definition=PromptAgentDefinition( + model=model_name, + instructions=AGENT_INSTRUCTIONS, + ), + ) + print(f"Agent created (id: {agent.id}, name: {agent.name}, version: {agent.version}).") + + try: + # ------------------------------------------------------------------ + # 2. Submit a SimpleQnA data generation job sourced from the agent. + # ------------------------------------------------------------------ + # The service fetches the agent's instructions / prompt and uses + # `model_options.model` to synthesize QnA pairs from them. + print(f"Create a SimpleQnA evaluation job sourced from agent `{agent.name}` (version {agent.version}).") + job = DataGenerationJob( + inputs=DataGenerationJobInputs( + name=f"simpleqna-agent-{run_id}", + scenario=DataGenerationJobScenario.EVALUATION, + sources=[ + AgentDataGenerationJobSource( + description="Agent definition (instructions / prompt) used to seed QnA generation.", + agent_name=agent.name, + agent_version=agent.version, + ), + ], + options=SimpleQnADataGenerationJobOptions( + # Service requires max_samples to be between 15 and 1000. + max_samples=15, + # `simple_qna` REQUIRES model_options. + model_options=DataGenerationModelOptions(model=model_name), + ), + output_options=DataGenerationJobOutputOptions(name=output_dataset_name), + ), + ) + job = project_client.beta.datasets.create_generation_job(job=job) + print(f"Created data generation job `{job.id}` (status: `{job.status}`).") + + print(f"Poll job `{job.id}` until it reaches a terminal state.", end="", flush=True) + while True: + job = project_client.beta.datasets.get_generation_job(job_id=job.id) + if job.status in TERMINAL_STATUSES: + break + time.sleep(poll_interval_seconds) + print(".", end="", flush=True) + print() + print(f"Final job status: `{job.status}`.") + + if job.status != JobStatus.SUCCEEDED: + message = job.error.message if job.error is not None else "" + raise RuntimeError(f"Job `{job.id}` ended with status `{job.status}`: {message}") + + # Locate the Dataset output produced by the job. + output_name: str = "" + output_version: str = "" + for output in (job.result.outputs if job.result is not None else None) or []: + if isinstance(output, DatasetDataGenerationJobOutput): + output_name = output.name or "" + output_version = output.version or "" + break + if not output_name or not output_version: + raise RuntimeError(f"Job `{job.id}` did not produce a dataset output.") + + dataset: DatasetVersion = project_client.datasets.get(name=output_name, version=output_version) + print(f"Generated dataset: name=`{dataset.name}` version=`{dataset.version}` id=`{dataset.id}`") + if job.result is not None and job.result.generated_samples is not None: + print(f"Generated samples: {job.result.generated_samples}") + + # ------------------------------------------------------------------ + # 3. Clean up the generated dataset and the data generation job + # (the agent is deleted in the `finally` block below). + # ------------------------------------------------------------------ + print(f"Delete the generated dataset `{dataset.name}` v{dataset.version}.") + project_client.datasets.delete(name=dataset.name or "", version=dataset.version or "") + + print(f"Delete the data generation job `{job.id}`.") + project_client.beta.datasets.delete_generation_job(job_id=job.id) + finally: + # The agent is short-lived — always delete it, even if the job failed. + print(f"Delete the prompt agent `{agent.name}` (version {agent.version}).") + project_client.agents.delete_version(agent_name=agent.name, agent_version=agent.version) diff --git a/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_file_source.py b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_file_source.py new file mode 100644 index 000000000000..f52d331e2603 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_file_source.py @@ -0,0 +1,238 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Generates an evaluation dataset from a multi-source `simple_qna` job that + combines an Azure OpenAI File with an inline Prompt. The sample: + + 1. Uploads a short Markdown reference document via the Azure OpenAI Files + API (`purpose=user_data`) so it can be referenced by file id. + 2. Creates a `DataGenerationJob` (scenario=EVALUATION, type=simple_qna) + with two sources: the uploaded `File` and a `Prompt` that adds an + instruction to generate expert-level, high-difficulty questions. + 3. Polls the job to completion, resolves the generated `DatasetVersion`, + and shows that the caller-supplied output `description` and `tags` are + propagated onto the new dataset. + 4. Cleans up the generated dataset, the Azure OpenAI input file, and the data generation job. + + `simple_qna` REQUIRES `model_options` — the service uses the configured LLM + to synthesize question / answer pairs from the combined sources. + + For `simple_qna` evaluation jobs the deployed model must support the + Azure OpenAI Responses API. See the supported-model list: + https://learn.microsoft.com/azure/foundry/openai/how-to/responses?tabs=python-key#model-support + +USAGE: + python sample_dataset_generation_job_simpleqna_with_file_source.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity openai python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found + in the overview page of your Microsoft Foundry project. + 2) FOUNDRY_MODEL_NAME - Required. The name of an Azure OpenAI model + deployment used to synthesize the QnA samples. For `simple_qna` evaluation, + choose a Responses-API capable model (see the link in the description). + 3) DATASET_NAME - Optional. Name to assign to the generated output dataset. + Defaults to `simpleqna-file-source-sample`. The service caps the rendered + output name at 50 characters, so keep custom values short — the sample + appends a unique run id suffix. + 4) POLL_INTERVAL_SECONDS - Optional. Number of seconds to sleep between status + polls for the data generation job. Defaults to 10. +""" + +import io +import os +import time +import uuid +from datetime import datetime, timezone + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import ( + DataGenerationJob, + DataGenerationJobInputs, + DataGenerationJobOutputOptions, + DataGenerationJobScenario, + DataGenerationModelOptions, + DatasetDataGenerationJobOutput, + DatasetVersion, + FileDataGenerationJobSource, + JobStatus, + PromptDataGenerationJobSource, + SimpleQnADataGenerationJobOptions, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_name = os.environ["FOUNDRY_MODEL_NAME"] +dataset_name = os.environ.get("DATASET_NAME", "simpleqna-file-source-sample") +poll_interval_seconds = int(os.environ.get("POLL_INTERVAL_SECONDS", "10")) + +# Unique per-run resource names so repeated runs do not collide. +# Output names are capped at 50 characters by the service. +run_id = f"{datetime.now(tz=timezone.utc).strftime('%y%m%d%H%M%S')}-{uuid.uuid4().hex[:4]}" +output_dataset_name = f"{dataset_name}-{run_id}" +if len(output_dataset_name) > 50: + raise ValueError( + f"Output dataset name `{output_dataset_name}` exceeds the 50-character service limit. " + f"Lower DATASET_NAME (currently `{dataset_name}`) so that `-` fits within 50 characters." + ) + +# Reference document the sample uploads as an Azure OpenAI file. The service +# requires the file to contain at least 1 KB of content to generate QnA from. +SEED_REFERENCE_DOCUMENT = """# Widgets and Gizmos Reference + +## Products +- Widget: blue, manufactured at Factory 7 in Acme, carbon-fiber, rated to 80 C, sold in packs of 4, 250 g each. +- Gizmo: red, manufactured at Factory 12 in Bedrock, carbon-fiber, rated to 80 C, sold individually, 1.2 kg each. +- Sprocket: green, manufactured at Factory 3 in Acme, stainless steel, rated to 200 C, sold individually, 500 g each. + +## Operations +- Factory operates weekdays 0700-1900 local time. +- Closed on public holidays, except for the annual maintenance run on December 27. +- ISO 9001 certified; audited annually by an independent third party. +- Quality control samples every 100th unit and runs full destructive testing on every 5000th unit. + +## Customer support +- Warranty claims: email support@example.com with the serial number printed on the underside of the product. +- Returns: accepted within 30 days if unopened; opened items are eligible for repair only. +- Bulk orders (50+ units): contact sales@example.com for volume pricing and an extended 90-day return window. +- Replacement parts: orderable directly from the support portal using the original order number. + +## Pricing and SLAs +- Widget pack: USD 24.99 per 4-pack; free shipping on orders over USD 75. +- Gizmo unit: USD 49.99; free shipping on orders over USD 75. +- Sprocket unit: USD 14.99; ships from regional warehouses in 1-2 business days. +- Standard support response: within one business day. Priority support response: within four hours. +""" + +EXPECTED_OUTPUT_DESCRIPTION = "Expert-level QnA pairs generated from the Widgets & Gizmos reference." +EXPECTED_OUTPUT_TAGS = {"sample": "dataset-generation-simpleqna-with-file-source", "difficulty": "expert"} + +TERMINAL_STATUSES = {JobStatus.SUCCEEDED, JobStatus.FAILED, JobStatus.CANCELLED} + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as openai_client, +): + + # ------------------------------------------------------------------ + # 1. Upload the seed reference document as an Azure OpenAI file. + # ------------------------------------------------------------------ + seed_filename = f"widgets-gizmos-seed-{run_id}.md" + print(f"Upload the seed reference document as Azure OpenAI file `{seed_filename}`.") + seed_file = openai_client.files.create( + file=(seed_filename, io.BytesIO(SEED_REFERENCE_DOCUMENT.encode("utf-8"))), + purpose="user_data", + ) + print(f"Uploaded Azure OpenAI file (id: {seed_file.id}).") + + # Wait for the file to finish processing — the data generation service + # rejects references to files that are not yet in the `processed` state. + print("Wait for the Azure OpenAI file to be processed.", end="", flush=True) + while seed_file.status not in ("processed", "error"): + time.sleep(2) + seed_file = openai_client.files.retrieve(file_id=seed_file.id) + print(".", end="", flush=True) + print() + if seed_file.status != "processed": + raise RuntimeError(f"Azure OpenAI file `{seed_file.id}` failed to process: status=`{seed_file.status}`.") + + # ------------------------------------------------------------------ + # 2. Submit a multi-source SimpleQnA data generation job. + # ------------------------------------------------------------------ + # Two sources are combined for a single job: + # - The File source contributes the source material (the reference + # document uploaded above). + # - The Prompt source contributes a steering instruction (difficulty). + print("Create a multi-source data generation job (File + Prompt).") + job = DataGenerationJob( + inputs=DataGenerationJobInputs( + name=f"simpleqna-multisource-{run_id}", + scenario=DataGenerationJobScenario.EVALUATION, + sources=[ + FileDataGenerationJobSource( + description="Widgets & Gizmos product / operations reference (Azure OpenAI file).", + id=seed_file.id, + ), + PromptDataGenerationJobSource( + description="Specifies the question difficulty for SimpleQnA generation.", + prompt="Generate expert-level questions of high difficulty.", + ), + ], + options=SimpleQnADataGenerationJobOptions( + # Service requires max_samples to be between 15 and 1000. + max_samples=15, + # `simple_qna` REQUIRES model_options. + model_options=DataGenerationModelOptions(model=model_name), + ), + output_options=DataGenerationJobOutputOptions( + name=output_dataset_name, + description=EXPECTED_OUTPUT_DESCRIPTION, + tags=EXPECTED_OUTPUT_TAGS, + ), + ), + ) + job = project_client.beta.datasets.create_generation_job(job=job) + print(f"Created data generation job `{job.id}` (status: `{job.status}`).") + + print(f"Poll job `{job.id}` until it reaches a terminal state.", end="", flush=True) + while True: + job = project_client.beta.datasets.get_generation_job(job_id=job.id) + if job.status in TERMINAL_STATUSES: + break + time.sleep(poll_interval_seconds) + print(".", end="", flush=True) + print() + print(f"Final job status: `{job.status}`.") + + if job.status != JobStatus.SUCCEEDED: + message = job.error.message if job.error is not None else "" + raise RuntimeError(f"Job `{job.id}` ended with status `{job.status}`: {message}") + + # Locate the Dataset output produced by the job. + output_name: str = "" + output_version: str = "" + for output in (job.result.outputs if job.result is not None else None) or []: + if isinstance(output, DatasetDataGenerationJobOutput): + output_name = output.name or "" + output_version = output.version or "" + break + if not output_name or not output_version: + raise RuntimeError(f"Job `{job.id}` did not produce a dataset output.") + + # ------------------------------------------------------------------ + # 3. Inspect the generated dataset and show metadata propagation. + # ------------------------------------------------------------------ + # The caller-supplied output `description` and `tags` are persisted onto + # the generated dataset. The service also automatically adds a + # `data_generation_job_id` tag pointing back at this job. + dataset: DatasetVersion = project_client.datasets.get(name=output_name, version=output_version) + print(f"Generated dataset: name=`{dataset.name}` version=`{dataset.version}` id=`{dataset.id}`") + print(f" description: {dataset.description}") + print(f" tags: {dataset.tags}") + if job.result is not None and job.result.generated_samples is not None: + print(f"Generated samples: {job.result.generated_samples}") + + # ------------------------------------------------------------------ + # 4. Clean up. + # ------------------------------------------------------------------ + print(f"Delete the generated dataset `{dataset.name}` v{dataset.version}.") + project_client.datasets.delete(name=dataset.name or "", version=dataset.version or "") + + print(f"Delete the Azure OpenAI input file `{seed_file.id}`.") + openai_client.files.delete(file_id=seed_file.id) + + print(f"Delete the data generation job `{job.id}`.") + project_client.beta.datasets.delete_generation_job(job_id=job.id) diff --git a/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_prompt_source.py b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_prompt_source.py new file mode 100644 index 000000000000..a4cb9bb00b68 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_simpleqna_with_prompt_source.py @@ -0,0 +1,272 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + End-to-end scenario combining `.beta.datasets` data generation with an + evaluation run. The sample: + + 1. Creates a `DataGenerationJob` (scenario=EVALUATION, type=simple_qna) that + synthesizes question/answer pairs from an inline prompt and writes them + to a new versioned Dataset. + 2. Polls the job to completion and resolves the resulting `DatasetVersion`. + 3. Creates an OpenAI evaluation (`client.evals.create`) with builtin + Azure AI evaluators. + 4. Runs the evaluation against the generated dataset by passing the + dataset's id as the run's `file_id`. + 5. Cleans up the evaluation, the generated dataset, and the data generation job. + +USAGE: + python sample_dataset_generation_job_simpleqna_with_prompt_source.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found + in the overview page of your Microsoft Foundry project. + 2) FOUNDRY_MODEL_NAME - Required. The name of an Azure OpenAI model + deployment used both to generate the QnA samples and as the judge model + for builtin evaluators. For `simple_qna` evaluation jobs the deployed + model must support the Azure OpenAI Responses API. See the supported-model + list: https://learn.microsoft.com/azure/foundry/openai/how-to/responses?tabs=python-key#model-support + 3) DATASET_NAME - Optional. Name to assign to the generated output dataset. + Defaults to `dataset-generation-eval-sample`. + 4) POLL_INTERVAL_SECONDS - Optional. Number of seconds to sleep between status + polls for both the data generation job and the evaluation run. Defaults to 10. +""" + +import os +import time + +from dotenv import load_dotenv +from openai.types.eval_create_params import DataSourceConfigCustom +from openai.types.evals.create_eval_completions_run_data_source_param import ( + CreateEvalCompletionsRunDataSourceParam, + InputMessagesTemplate, + InputMessagesTemplateTemplateEvalItem, + SourceFileID, +) +from openai.types.responses.response_input_text_param import ResponseInputTextParam + +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import ( + DataGenerationJob, + DataGenerationJobInputs, + DataGenerationJobOutputOptions, + DataGenerationJobScenario, + DataGenerationModelOptions, + DatasetDataGenerationJobOutput, + DatasetVersion, + JobStatus, + PromptDataGenerationJobSource, + SimpleQnADataGenerationJobOptions, + TestingCriterionAzureAIEvaluator, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_name = os.environ["FOUNDRY_MODEL_NAME"] +dataset_name = os.environ.get("DATASET_NAME", "dataset-generation-eval-sample") +poll_interval_seconds = int(os.environ.get("POLL_INTERVAL_SECONDS", "10")) + +TERMINAL_STATUSES = {JobStatus.SUCCEEDED, JobStatus.FAILED, JobStatus.CANCELLED} + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as openai_client, +): + + # ------------------------------------------------------------------ + # 1. Generate a QnA evaluation dataset from an inline prompt. + # ------------------------------------------------------------------ + print("Create a data generation job.") + job = DataGenerationJob( + inputs=DataGenerationJobInputs( + name="qna-from-policy-prompt", + scenario=DataGenerationJobScenario.EVALUATION, + sources=[ + PromptDataGenerationJobSource( + description="Contoso refund policy", + prompt=( + "Contoso offers a full refund within 30 days of purchase for any product " + "returned in its original condition. After 30 days, store credit may be " + "issued at the discretion of customer support. Digital goods are " + "non-refundable once downloaded." + ), + ), + ], + options=SimpleQnADataGenerationJobOptions( + # Service requires max_samples to be between 15 and 1000. + max_samples=15, + model_options=DataGenerationModelOptions(model=model_name), + ), + output_options=DataGenerationJobOutputOptions( + name=dataset_name, + description="QnA pairs generated from the Contoso refund policy prompt.", + tags={"sample": "dataset-generation-with-evaluation"}, + ), + ), + ) + job = project_client.beta.datasets.create_generation_job(job=job) + print(f"Created data generation job `{job.id}` (status: `{job.status}`).") + + print(f"Poll job `{job.id}` until it reaches a terminal state.", end="", flush=True) + while True: + job = project_client.beta.datasets.get_generation_job(job_id=job.id) + if job.status in TERMINAL_STATUSES: + break + time.sleep(poll_interval_seconds) + print(".", end="", flush=True) + print() + print(f"Final job status: `{job.status}`.") + + if job.status != JobStatus.SUCCEEDED: + message = job.error.message if job.error is not None else "" + raise RuntimeError(f"Job `{job.id}` ended with status `{job.status}`: {message}") + + # Locate the Dataset output produced by the job. + output_name: str = "" + output_version: str = "" + for output in (job.result.outputs if job.result is not None else None) or []: + if isinstance(output, DatasetDataGenerationJobOutput): + output_name = output.name or "" + output_version = output.version or "" + break + if not output_name or not output_version: + raise RuntimeError(f"Job `{job.id}` did not produce a dataset output.") + + # Resolve the DatasetVersion so we can use its id as the eval run's file_id. + dataset: DatasetVersion = project_client.datasets.get(name=output_name, version=output_version) + print(f"Generated dataset: name=`{dataset.name}` version=`{dataset.version}` id=`{dataset.id}`") + + # ------------------------------------------------------------------ + # 2. Create an evaluation that scores the model's answers to the + # generated questions. + # ------------------------------------------------------------------ + # The `simple_qna` generator produces records with `query` and `ground_truth` + # fields. The evaluation run below sends each `query` through a model target + # to produce a response, and the evaluators score that response. + data_source_config = DataSourceConfigCustom( + type="custom", + item_schema={ + "type": "object", + "properties": { + "query": {"type": "string"}, + "ground_truth": {"type": "string"}, + }, + "required": ["query"], + }, + include_sample_schema=True, + ) + + testing_criteria = [ + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="coherence", + evaluator_name="builtin.coherence", + initialization_parameters={"deployment_name": model_name}, + data_mapping={"query": "{{item.query}}", "response": "{{sample.output_text}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="fluency", + evaluator_name="builtin.fluency", + initialization_parameters={"deployment_name": model_name}, + data_mapping={"response": "{{sample.output_text}}"}, + ), + ] + + print("Create the evaluation.") + eval_object = openai_client.evals.create( + name="generated-qna-evaluation", + data_source_config=data_source_config, + testing_criteria=testing_criteria, + ) + print(f"Evaluation created (id: {eval_object.id}).") + + # ------------------------------------------------------------------ + # 3. Run the evaluation against the generated dataset. + # ------------------------------------------------------------------ + # `completions` data source: for each item in the dataset, render the + # input_messages template, send to `model`, and feed the response into + # the testing criteria as `sample.output_text`. + print(f"Create an evaluation run that consumes dataset `{dataset.id}`.") + input_message = InputMessagesTemplate( + type="template", + template=[ + InputMessagesTemplateTemplateEvalItem( + type="message", + role="developer", + content=ResponseInputTextParam( + type="input_text", + text=( + "You are a Contoso customer-support assistant. Answer the user's " + "question about the Contoso refund policy clearly and concisely." + ), + ), + ), + InputMessagesTemplateTemplateEvalItem( + type="message", + role="user", + content=ResponseInputTextParam( + type="input_text", + text="{{item.query}}", + ), + ), + ], + ) + data_source = CreateEvalCompletionsRunDataSourceParam( + type="completions", + source=SourceFileID(type="file_id", id=dataset.id or ""), + input_messages=input_message, + model=model_name, + ) + eval_run = openai_client.evals.runs.create( + eval_id=eval_object.id, + name="generated-qna-evaluation-run", + data_source=data_source, + ) + print(f"Evaluation run created (id: {eval_run.id}).") + + while eval_run.status not in ("completed", "failed"): + time.sleep(poll_interval_seconds) + eval_run = openai_client.evals.runs.retrieve(run_id=eval_run.id, eval_id=eval_object.id) + print(f"Final eval run status: `{eval_run.status}`.") + + if eval_run.status == "completed": + print(f"Result counts: {eval_run.result_counts}") + print(f"Eval run report URL: {eval_run.report_url}") + output_items = list(openai_client.evals.runs.output_items.list(run_id=eval_run.id, eval_id=eval_object.id)) + print(f"Output items (total: {len(output_items)}):") + # Print a per-item summary (avoid pprint on the full payload to keep the + # output ASCII-safe on Windows consoles and easy to scan). + for idx, item in enumerate(output_items, start=1): + results = getattr(item, "results", None) or [] + scores = ", ".join( + f"{r.get('name', '?')}={r.get('score', 'n/a')} ({r.get('passed', 'n/a')})" + for r in results + if isinstance(r, dict) + ) + print(f" item {idx}: status={item.status} | {scores}") + else: + print("Evaluation run did not complete successfully.") + + # ------------------------------------------------------------------ + # 4. Clean up. + # ------------------------------------------------------------------ + print(f"Delete evaluation `{eval_object.id}`.") + openai_client.evals.delete(eval_id=eval_object.id) + + print(f"Delete the generated dataset `{dataset.name}` v{dataset.version}.") + project_client.datasets.delete(name=dataset.name or "", version=dataset.version or "") + + print(f"Delete the data generation job `{job.id}`.") + project_client.beta.datasets.delete_generation_job(job_id=job.id) diff --git a/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_traces_for_evaluation.py b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_traces_for_evaluation.py new file mode 100644 index 000000000000..9b2ca86a89bf --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_traces_for_evaluation.py @@ -0,0 +1,160 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Generates an evaluation dataset from an agent's recent conversation + traces. The sample: + + 1. Creates a `DataGenerationJob` (scenario=EVALUATION, type=traces) that + reads spans from Application Insights for an existing agent within a + time window and synthesizes question / answer pairs into a new + versioned Dataset. + 2. Polls the job to completion and resolves the resulting `DatasetVersion`. + 3. Cleans up the generated dataset and the data generation job. + + The Traces source consumes existing telemetry, so no `model_options` are + required — the service derives samples directly from the agent's traces. + The agent must have at least one trace recorded within the configured + look-back window or the job will succeed with zero generated samples. + +USAGE: + python sample_dataset_generation_job_traces_for_evaluation.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found + in the overview page of your Microsoft Foundry project. + 2) FOUNDRY_AGENT_NAME - Required. The name of an agent (Foundry Agent or + OpenTelemetry-instrumented third-party agent) that has recent + conversation traces in Application Insights. + 3) DATASET_NAME - Optional. Name to assign to the generated output dataset. + Defaults to `traces-eval-sample`. The service caps the rendered output + name at 50 characters, so keep custom values short — the sample appends + a unique run id suffix. + 4) FOUNDRY_TRACES_WINDOW_DAYS - Optional. How far back, in days, to look for + agent traces. Defaults to 7. + 5) POLL_INTERVAL_SECONDS - Optional. Number of seconds to sleep between status + polls for the data generation job. Defaults to 10. +""" + +import os +import time +import uuid +from datetime import datetime, timedelta, timezone + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import ( + DataGenerationJob, + DataGenerationJobInputs, + DataGenerationJobOutputOptions, + DataGenerationJobScenario, + DatasetDataGenerationJobOutput, + DatasetVersion, + JobStatus, + TracesDataGenerationJobOptions, + TracesDataGenerationJobSource, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +agent_name = os.environ["FOUNDRY_AGENT_NAME"] +dataset_name = os.environ.get("DATASET_NAME", "traces-eval-sample") +traces_window_days = int(os.environ.get("FOUNDRY_TRACES_WINDOW_DAYS", "7")) +poll_interval_seconds = int(os.environ.get("POLL_INTERVAL_SECONDS", "10")) + +# Unique per-run output dataset name so repeated runs do not collide. +# Output names are capped at 50 characters by the service. +run_id = f"{datetime.now(tz=timezone.utc).strftime('%y%m%d%H%M%S')}-{uuid.uuid4().hex[:4]}" +output_dataset_name = f"{dataset_name}-{run_id}" +if len(output_dataset_name) > 50: + raise ValueError( + f"Output dataset name `{output_dataset_name}` exceeds the 50-character service limit. " + f"Lower DATASET_NAME (currently `{dataset_name}`) so that `-` fits within 50 characters." + ) + +# Trace look-back window: now - `traces_window_days` ... now. +end_time = datetime.now(tz=timezone.utc) +start_time = end_time - timedelta(days=traces_window_days) + +TERMINAL_STATUSES = {JobStatus.SUCCEEDED, JobStatus.FAILED, JobStatus.CANCELLED} + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, +): + + # ------------------------------------------------------------------ + # 1. Submit a data generation job that reads agent traces. + # ------------------------------------------------------------------ + print(f"Create a data generation job from traces for agent `{agent_name}` (window: {traces_window_days} day(s)).") + job = DataGenerationJob( + inputs=DataGenerationJobInputs( + name=f"traces-eval-{run_id}", + scenario=DataGenerationJobScenario.EVALUATION, + sources=[ + TracesDataGenerationJobSource( + description="Application Insights conversation traces for the Foundry agent.", + agent_name=agent_name, + start_time=start_time, + end_time=end_time, + ), + ], + options=TracesDataGenerationJobOptions( + # Service requires max_samples to be between 15 and 1000. + max_samples=15, + ), + output_options=DataGenerationJobOutputOptions(name=output_dataset_name), + ), + ) + job = project_client.beta.datasets.create_generation_job(job=job) + print(f"Created data generation job `{job.id}` (status: `{job.status}`).") + + print(f"Poll job `{job.id}` until it reaches a terminal state.", end="", flush=True) + while True: + job = project_client.beta.datasets.get_generation_job(job_id=job.id) + if job.status in TERMINAL_STATUSES: + break + time.sleep(poll_interval_seconds) + print(".", end="", flush=True) + print() + print(f"Final job status: `{job.status}`.") + + if job.status != JobStatus.SUCCEEDED: + message = job.error.message if job.error is not None else "" + raise RuntimeError(f"Job `{job.id}` ended with status `{job.status}`: {message}") + + # Locate the Dataset output produced by the job. + output_name: str = "" + output_version: str = "" + for output in (job.result.outputs if job.result is not None else None) or []: + if isinstance(output, DatasetDataGenerationJobOutput): + output_name = output.name or "" + output_version = output.version or "" + break + if not output_name or not output_version: + raise RuntimeError(f"Job `{job.id}` did not produce a dataset output.") + + dataset: DatasetVersion = project_client.datasets.get(name=output_name, version=output_version) + print(f"Generated dataset: name=`{dataset.name}` version=`{dataset.version}` id=`{dataset.id}`") + if job.result is not None and job.result.generated_samples is not None: + print(f"Generated samples: {job.result.generated_samples}") + + # ------------------------------------------------------------------ + # 2. Clean up. + # ------------------------------------------------------------------ + print(f"Delete the generated dataset `{dataset.name}` v{dataset.version}.") + project_client.datasets.delete(name=dataset.name or "", version=dataset.version or "") + + print(f"Delete the data generation job `{job.id}`.") + project_client.beta.datasets.delete_generation_job(job_id=job.id) diff --git a/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_traces_for_finetuning.py b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_traces_for_finetuning.py new file mode 100644 index 000000000000..d4f7cf4c1a73 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/datasets/sample_dataset_generation_job_traces_for_finetuning.py @@ -0,0 +1,173 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Generates supervised fine-tuning data from an agent's recent + conversation traces. The sample: + + 1. Creates a `DataGenerationJob` (scenario=SUPERVISED_FINETUNING, + type=traces) that reads spans from Application Insights for an + existing agent within a time window and emits ready-to-use fine-tuning + JSONL files split into training and validation partitions. + 2. Polls the job to completion and prints every generated file output. + 3. Cleans up the generated fine-tuning files and the data generation job. + + Setting `train_split` triggers a split of the generated samples into two + Azure OpenAI files — a training partition and a validation partition. + The Traces source consumes existing telemetry, so no `model_options` are + required. + +USAGE: + python sample_dataset_generation_job_traces_for_finetuning.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found + in the overview page of your Microsoft Foundry project. + 2) FOUNDRY_AGENT_NAME - Required. The name of an agent (Foundry Agent or + OpenTelemetry-instrumented third-party agent) that has recent + conversation traces in Application Insights. + 3) DATASET_NAME - Optional. Name to assign to the generated output files + (used as the file name prefix). Defaults to `traces-finetuning-sample`. + The service caps the rendered output name at 50 characters, so keep + custom values short — the sample appends a unique run id suffix. + 4) FOUNDRY_TRACES_WINDOW_DAYS - Optional. How far back, in days, to look for + agent traces. Defaults to 7. + 5) POLL_INTERVAL_SECONDS - Optional. Number of seconds to sleep between status + polls for the data generation job. Defaults to 10. +""" + +import os +import time +import uuid +from datetime import datetime, timedelta, timezone + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import ( + DataGenerationJob, + DataGenerationJobInputs, + DataGenerationJobOutputOptions, + DataGenerationJobScenario, + FileDataGenerationJobOutput, + JobStatus, + TracesDataGenerationJobOptions, + TracesDataGenerationJobSource, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +agent_name = os.environ["FOUNDRY_AGENT_NAME"] +dataset_name = os.environ.get("DATASET_NAME", "traces-finetuning-sample") +traces_window_days = int(os.environ.get("FOUNDRY_TRACES_WINDOW_DAYS", "7")) +poll_interval_seconds = int(os.environ.get("POLL_INTERVAL_SECONDS", "10")) + +# Unique per-run output name so repeated runs do not collide. +# Output names are capped at 50 characters by the service. +run_id = f"{datetime.now(tz=timezone.utc).strftime('%y%m%d%H%M%S')}-{uuid.uuid4().hex[:4]}" +output_name = f"{dataset_name}-{run_id}" +if len(output_name) > 50: + raise ValueError( + f"Output name `{output_name}` exceeds the 50-character service limit. " + f"Lower DATASET_NAME (currently `{dataset_name}`) so that `-` fits within 50 characters." + ) + +# Trace look-back window: now - `traces_window_days` ... now. +end_time = datetime.now(tz=timezone.utc) +start_time = end_time - timedelta(days=traces_window_days) + +TERMINAL_STATUSES = {JobStatus.SUCCEEDED, JobStatus.FAILED, JobStatus.CANCELLED} + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as openai_client, +): + + # ------------------------------------------------------------------ + # 1. Submit a fine-tuning data generation job that reads agent traces. + # ------------------------------------------------------------------ + print( + f"Create a fine-tuning data generation job from traces for agent `{agent_name}` (window: {traces_window_days} day(s))." + ) + job = DataGenerationJob( + inputs=DataGenerationJobInputs( + name=f"traces-finetuning-{run_id}", + scenario=DataGenerationJobScenario.SUPERVISED_FINETUNING, + sources=[ + TracesDataGenerationJobSource( + description="Application Insights conversation traces for the Foundry agent.", + agent_name=agent_name, + start_time=start_time, + end_time=end_time, + ), + ], + options=TracesDataGenerationJobOptions( + # Service requires max_samples to be between 15 and 1000. + max_samples=15, + # Split generated samples 80% training / 20% validation. + train_split=0.8, + ), + output_options=DataGenerationJobOutputOptions(name=output_name), + ), + ) + job = project_client.beta.datasets.create_generation_job(job=job) + print(f"Created data generation job `{job.id}` (status: `{job.status}`).") + + print(f"Poll job `{job.id}` until it reaches a terminal state.", end="", flush=True) + while True: + job = project_client.beta.datasets.get_generation_job(job_id=job.id) + if job.status in TERMINAL_STATUSES: + break + time.sleep(poll_interval_seconds) + print(".", end="", flush=True) + print() + print(f"Final job status: `{job.status}`.") + + if job.status != JobStatus.SUCCEEDED: + message = job.error.message if job.error is not None else "" + raise RuntimeError(f"Job `{job.id}` ended with status `{job.status}`: {message}") + + # ------------------------------------------------------------------ + # 2. Inspect the generated fine-tuning file outputs. + # ------------------------------------------------------------------ + # `train_split=0.8` produces two Azure OpenAI files: a training partition + # and a validation partition. Both are emitted as FileDataGenerationJobOutput + # entries in `job.result.outputs`. + file_outputs = [ + output + for output in ((job.result.outputs if job.result is not None else None) or []) + if isinstance(output, FileDataGenerationJobOutput) + ] + if not file_outputs: + raise RuntimeError(f"Job `{job.id}` did not produce any file outputs.") + + print(f"Generated {len(file_outputs)} fine-tuning file(s):") + for output in file_outputs: + if not output.id: + raise RuntimeError(f"Job `{job.id}` returned a file output without an id.") + # Resolve the Azure OpenAI file to surface its real filename and size. + file_info = openai_client.files.retrieve(file_id=output.id) + print(f" - filename=`{file_info.filename}` id=`{output.id}` bytes={file_info.bytes}") + if job.result is not None and job.result.generated_samples is not None: + print(f"Generated samples: {job.result.generated_samples}") + + # ------------------------------------------------------------------ + # 3. Clean up. + # ------------------------------------------------------------------ + for output in file_outputs: + print(f"Delete the generated Azure OpenAI file `{output.id}`.") + openai_client.files.delete(file_id=output.id) + + print(f"Delete the data generation job `{job.id}`.") + project_client.beta.datasets.delete_generation_job(job_id=job.id) diff --git a/sdk/ai/azure-ai-projects/samples/datasets/sample_datasets.py b/sdk/ai/azure-ai-projects/samples/datasets/sample_datasets.py index 791b33754479..8860575516f2 100644 --- a/sdk/ai/azure-ai-projects/samples/datasets/sample_datasets.py +++ b/sdk/ai/azure-ai-projects/samples/datasets/sample_datasets.py @@ -62,7 +62,6 @@ "or set CONNECTION_NAME explicitly." ) from e - # [START datasets_sample] print( f"Upload a single file and create a new Dataset `{dataset_name}`, version `{dataset_version_1}`, to reference the file." ) @@ -105,4 +104,3 @@ print("Delete all Dataset versions created above:") project_client.datasets.delete(name=dataset_name, version=dataset_version_1) project_client.datasets.delete(name=dataset_name, version=dataset_version_2) - # [END dataset_sample] diff --git a/sdk/ai/azure-ai-projects/samples/deployments/sample_deployments.py b/sdk/ai/azure-ai-projects/samples/deployments/sample_deployments.py index 203015acdad7..2d46858fc7c4 100644 --- a/sdk/ai/azure-ai-projects/samples/deployments/sample_deployments.py +++ b/sdk/ai/azure-ai-projects/samples/deployments/sample_deployments.py @@ -41,7 +41,6 @@ AIProjectClient(endpoint=endpoint, credential=credential) as project_client, ): - # [START deployments_sample] print("List all deployments:") for deployment in project_client.deployments.list(): print(deployment) @@ -68,4 +67,3 @@ print(f"Capabilities: {deployment.capabilities}") print(f"SKU: {deployment.sku}") print(f"Connection Name: {deployment.connection_name}") - # [END deployments_sample] diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/README.md b/sdk/ai/azure-ai-projects/samples/evaluations/README.md index d601e9e22906..9a3dc1dc6f45 100644 --- a/sdk/ai/azure-ai-projects/samples/evaluations/README.md +++ b/sdk/ai/azure-ai-projects/samples/evaluations/README.md @@ -80,6 +80,16 @@ These samples require additional setup or Azure services: | [sample_eval_catalog_code_based_evaluators.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/evaluations/sample_eval_catalog_code_based_evaluators.py) | Custom code-based (inline) evaluators | | [sample_eval_catalog_prompt_based_evaluators.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/evaluations/sample_eval_catalog_prompt_based_evaluators.py) | Custom prompt-based evaluators | +### Rubric Evaluators + +| Sample | Description | +|--------|-------------| +| [sample_rubric_evaluator_manual.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_manual.py) | Hand-author a rubric evaluator (dimensions, weights, pass threshold) with `create_version`, then use it in an OpenAI eval run | +| [sample_rubric_evaluator_generation_basic.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_basic.py) | Generate an evaluator from a single prompt source, inspect the produced dimensions, and use the auto-saved evaluator in an eval run | +| [sample_rubric_evaluator_generation_all_sources.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_all_sources.py) | Exercise every source type — prompt + agent + dataset in a combined job, plus a separate traces + agent-companion job | +| [sample_rubric_evaluator_generation_iterate.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_iterate.py) | Human-in-the-loop iteration: generate v1, edit dimensions locally, save as v2 with `create_version` | +| [sample_rubric_evaluator_generation_lifecycle.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_lifecycle.py) | Full generation-job lifecycle: idempotent `create_generation_job`, polling, list, and delete | + ### Agentic Evaluators Located in the [agentic_evaluators](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/ai/azure-ai-projects/samples/evaluations/agentic_evaluators) subfolder: diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/data_folder/sample_data_multiturn_conversations.jsonl b/sdk/ai/azure-ai-projects/samples/evaluations/data_folder/sample_data_multiturn_conversations.jsonl new file mode 100644 index 000000000000..7f1aa206e38d --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/evaluations/data_folder/sample_data_multiturn_conversations.jsonl @@ -0,0 +1,3 @@ +{"messages": [{"role": "system", "content": "You are a helpful travel assistant."}, {"role": "user", "content": "I need to book a flight to Paris."}, {"role": "assistant", "content": "I'd be happy to help you book a flight to Paris. What dates are you looking to travel?"}, {"role": "user", "content": "Next Friday, returning Sunday."}, {"role": "assistant", "content": "I found several options for flights departing next Friday and returning Sunday. The best value is a direct flight on Air France for $450 round trip. Would you like me to book this for you?"}]} +{"messages": [{"role": "user", "content": "What's the weather in Paris?"}, {"role": "assistant", "content": null, "tool_calls": [{"id": "call_123", "type": "function", "function": {"name": "get_weather", "arguments": "{\"location\": \"Paris\"}"}}]}, {"role": "tool", "tool_call_id": "call_123", "content": "{\"temperature\": 18, \"condition\": \"sunny\"}"}, {"role": "assistant", "content": "The weather in Paris is currently sunny with a temperature of 18\u00b0C (64\u00b0F)."}], "tool_definitions": [{"name": "get_weather", "description": "Get current weather for a location", "parameters": {"type": "object", "properties": {"location": {"type": "string"}}}}]} +{"messages": [{"role": "system", "content": "You are a customer service agent for a software company."}, {"role": "user", "content": "I can't log into my account. I've tried resetting my password but the email never arrives."}, {"role": "assistant", "content": "I'm sorry to hear you're having trouble logging in. Let me help you with that. Can you confirm the email address associated with your account?"}, {"role": "user", "content": "It's john.doe@example.com"}, {"role": "assistant", "content": "Thank you. I can see that the reset emails are being sent to john.doe@example.com but they appear to be bouncing. It looks like there may be a typo in the email on file. Would you like me to update it?"}, {"role": "user", "content": "Oh yes, it should be john.doe@example.org"}, {"role": "assistant", "content": "I've updated your email to john.doe@example.org and sent a new password reset link. You should receive it within the next few minutes. Is there anything else I can help you with?"}]} diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/data_folder/sample_data_simulation_scenarios.jsonl b/sdk/ai/azure-ai-projects/samples/evaluations/data_folder/sample_data_simulation_scenarios.jsonl new file mode 100644 index 000000000000..a5ccbced6e9d --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/evaluations/data_folder/sample_data_simulation_scenarios.jsonl @@ -0,0 +1,3 @@ +{"id": "scenario-1-greeting", "test_case_description": "User starts with a casual greeting; agent should respond warmly and offer help.", "desired_num_turns": 3} +{"id": "scenario-2-weather-followup", "test_case_description": "User asks about the weather in a major city, then asks a follow-up about whether to bring an umbrella.", "desired_num_turns": 4} +{"id": "scenario-3-store-hours", "test_case_description": "User asks if a store is open, then progressively narrows down to curbside-pickup hours, then places an order.", "desired_num_turns": 5} diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_agent_evaluation.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_agent_evaluation.py index 4882737ce719..77a2199cd7bb 100644 --- a/sdk/ai/azure-ai-projects/samples/evaluations/sample_agent_evaluation.py +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_agent_evaluation.py @@ -48,7 +48,6 @@ endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] model_deployment_name = os.environ.get("FOUNDRY_MODEL_NAME", "") # Sample : gpt-4o-mini -# [START agent_evaluation_basic] with ( DefaultAzureCredential() as credential, AIProjectClient(endpoint=endpoint, credential=credential) as project_client, @@ -126,7 +125,6 @@ eval_id=eval_object.id, name=f"Evaluation Run for Agent {agent.name}", data_source=data_source # type: ignore ) print(f"Evaluation run created (id: {agent_eval_run.id})") - # [END agent_evaluation_basic] while agent_eval_run.status not in ["completed", "failed"]: agent_eval_run = openai_client.evals.runs.retrieve(run_id=agent_eval_run.id, eval_id=eval_object.id) diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_builtin_with_traces.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_builtin_with_traces.py index dd2271d4d6b7..a5e88ab53301 100644 --- a/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_builtin_with_traces.py +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_builtin_with_traces.py @@ -7,12 +7,21 @@ """ DESCRIPTION: Given an AIProjectClient, this sample demonstrates how to run Azure AI Evaluations - against agent traces collected in Azure Application Insights. The sample fetches - trace IDs for a given agent and time range, creates an evaluation group configured - for trace analysis, and monitors the evaluation run until it completes. + against agent traces collected in Azure Application Insights. + + Supports three modes: + - Default mode (no flags): Queries Application Insights client-side for trace IDs + using the AGENT_ID environment variable, then passes them to the eval service. + - Agent ID mode (--agent-id): Passes the agent ID directly to the eval service, + which resolves traces server-side from Application Insights. + - Trace ID mode (--trace-ids): Passes explicit trace IDs to the eval service. USAGE: python sample_evaluations_builtin_with_traces.py + python sample_evaluations_builtin_with_traces.py --agent-id "my-agent:1" + python sample_evaluations_builtin_with_traces.py --trace-ids abc123 def456 + python sample_evaluations_builtin_with_traces.py --agent-id "my-agent:1" --lookback-hours 48 --max-traces 20 + python sample_evaluations_builtin_with_traces.py --no-cleanup Before running the sample: @@ -21,7 +30,8 @@ Set these environment variables with your own values: 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found in the overview page of your Microsoft Foundry project. It has the form: https://.services.ai.azure.com/api/projects/. - 2) APPINSIGHTS_RESOURCE_ID - Required. The Azure Application Insights resource ID that stores agent traces. + 2) APPINSIGHTS_RESOURCE_ID - Required (for default mode). The Azure Application Insights resource ID that stores + agent traces. Not needed when using --agent-id or --trace-ids. It has the form: /subscriptions//resourceGroups//providers/Microsoft.Insights/components/. 3) AGENT_ID - Required. The agent identifier emitted by the Azure tracing integration, used to filter traces. 4) FOUNDRY_MODEL_NAME - Required. The Azure OpenAI deployment name to use with the built-in evaluators. @@ -29,19 +39,18 @@ Defaults to 1. """ +import argparse import os import time from datetime import datetime, timedelta, timezone from pprint import pprint -from typing import Any, Dict, List +from typing import Any, Dict, List, Optional from dotenv import load_dotenv from azure.identity import DefaultAzureCredential from azure.monitor.query import LogsQueryClient, LogsQueryStatus from azure.ai.projects import AIProjectClient from azure.ai.projects.models import ( - AzureAIDataSourceConfig, TestingCriterionAzureAIEvaluator, - TracesPreviewEvalRunDataSource, ) load_dotenv() @@ -53,7 +62,7 @@ ] # Sample : /subscriptions//resourceGroups//providers/Microsoft.Insights/components/ agent_id = os.environ["AGENT_ID"] model_deployment_name = os.environ["FOUNDRY_MODEL_NAME"] -trace_query_hours = int(os.environ.get("TRACE_LOOKBACK_HOURS", "1")) +default_lookback_hours = int(os.environ.get("TRACE_LOOKBACK_HOURS", "1")) def _build_evaluator_config(name: str, evaluator_name: str) -> TestingCriterionAzureAIEvaluator: @@ -63,9 +72,9 @@ def _build_evaluator_config(name: str, evaluator_name: str) -> TestingCriterionA name=name, evaluator_name=evaluator_name, data_mapping={ - "query": "{{query}}", - "response": "{{response}}", - "tool_definitions": "{{tool_definitions}}", + "query": "{{sample.query}}", + "response": "{{sample.response}}", + "tool_definitions": "{{sample.tool_definitions}}", }, initialization_parameters={ "deployment_name": model_deployment_name, @@ -121,92 +130,133 @@ def get_trace_ids( return [] -def main() -> None: - end_time = datetime.now(tz=timezone.utc) - start_time = end_time - timedelta(hours=trace_query_hours) +def main() -> None: # pylint: disable=too-many-statements + parser = argparse.ArgumentParser(description="Run Azure AI trace evaluations against agent traces.") + mode = parser.add_mutually_exclusive_group() + mode.add_argument("--agent-id", default=None, help="Agent ID for server-side trace resolution") + mode.add_argument("--trace-ids", nargs="+", default=None, help="Explicit trace IDs to evaluate") + parser.add_argument("--lookback-hours", type=int, default=None, help="Lookback window in hours") + parser.add_argument("--max-traces", type=int, default=50, help="Max traces in agent-id mode (default: 50)") + parser.add_argument("--no-cleanup", action="store_true", help="Keep eval group after run") + args = parser.parse_args() + + lookback_hours = args.lookback_hours or default_lookback_hours + trace_ids: Optional[List[str]] = None + agent_id_for_server: Optional[str] = None + metadata: Dict[str, str] = {} + + if args.agent_id: + agent_id_for_server = args.agent_id + print("Mode: Server-side agent ID resolution") + print(f"Agent ID: {args.agent_id}") + print(f"Lookback: {lookback_hours}h, Max traces: {args.max_traces}") + metadata["agent_id"] = args.agent_id + + elif args.trace_ids: + trace_ids = list(args.trace_ids) + print(f"Mode: Explicit trace IDs ({len(trace_ids)} provided)") + + else: + end_time = datetime.now(tz=timezone.utc) + start_time = end_time - timedelta(hours=lookback_hours) - print("Querying Application Insights for trace identifiers...") - print(f"Agent ID: {agent_id}") - print(f"Time range: {start_time.isoformat()} to {end_time.isoformat()}") + print("Querying Application Insights for trace identifiers...") + print(f"Agent ID: {agent_id}") + print(f"Time range: {start_time.isoformat()} to {end_time.isoformat()}") - trace_ids = get_trace_ids(appinsights_resource_id, agent_id, start_time, end_time) + trace_ids = get_trace_ids(appinsights_resource_id, agent_id, start_time, end_time) - if not trace_ids: - print("No trace IDs found for the provided agent and time window.") - return + if not trace_ids: + print("No trace IDs found for the provided agent and time window.") + return - print(f"\nFound {len(trace_ids)} trace IDs:") - for trace_id in trace_ids: - print(f" - {trace_id}") + print(f"\nFound {len(trace_ids)} trace IDs:") + for tid in trace_ids: + print(f" - {tid}") + + metadata["agent_id"] = agent_id + metadata["start_time"] = start_time.isoformat() + metadata["end_time"] = end_time.isoformat() with DefaultAzureCredential() as credential: with AIProjectClient(endpoint=endpoint, credential=credential) as project_client: client = project_client.get_openai_client() - data_source_config = AzureAIDataSourceConfig( - type="azure_ai_source", - scenario="traces", - ) - testing_criteria = [ - _build_evaluator_config( - name="intent_resolution", - evaluator_name="builtin.intent_resolution", - ), - _build_evaluator_config( - name="task_adherence", - evaluator_name="builtin.task_adherence", - ), - ] - - print("\nCreating evaluation") - eval_object = client.evals.create( - name="agent_trace_eval_group", - data_source_config=data_source_config, - testing_criteria=testing_criteria, - ) - print(f"Evaluation created (id: {eval_object.id}, name: {eval_object.name})") - - print("\nGet Evaluation by Id") - eval_object_response = client.evals.retrieve(eval_object.id) - print("Evaluation Response:") - pprint(eval_object_response) - - print("\nCreating Eval Run with trace IDs") - run_name = f"agent_trace_eval_{datetime.now().strftime('%Y%m%d_%H%M%S')}" - data_source = TracesPreviewEvalRunDataSource( - type="azure_ai_traces_preview", - trace_ids=trace_ids, - lookback_hours=trace_query_hours, - ) - eval_run_object = client.evals.runs.create( - eval_id=eval_object.id, - name=run_name, - metadata={ - "agent_id": agent_id, - "start_time": start_time.isoformat(), - "end_time": end_time.isoformat(), - }, - data_source=data_source, - ) - print("Eval Run created") - pprint(eval_run_object) - - print("\nMonitoring Eval Run status...") - while True: - run = client.evals.runs.retrieve(run_id=eval_run_object.id, eval_id=eval_object.id) - print(f"Status: {run.status}") - - if run.status in {"completed", "failed", "canceled"}: - print("\nEval Run finished!") - print("Final Eval Run Response:") - pprint(run) - break - - time.sleep(5) - print("Waiting for eval run to complete...") - - client.evals.delete(eval_id=eval_object.id) - print("Evaluation deleted") + data_source_config = { + "type": "azure_ai_source", + "scenario": "traces", + } + + testing_criteria = [ + _build_evaluator_config( + name="intent_resolution", + evaluator_name="builtin.intent_resolution", + ), + _build_evaluator_config( + name="task_adherence", + evaluator_name="builtin.task_adherence", + ), + ] + + print("\nCreating evaluation") + eval_object = client.evals.create( + name="agent_trace_eval_group", + data_source_config=data_source_config, # type: ignore + testing_criteria=testing_criteria, # type: ignore + ) + print(f"Evaluation created (id: {eval_object.id}, name: {eval_object.name})") + + print("\nGet Evaluation by Id") + eval_object_response = client.evals.retrieve(eval_object.id) + print("Evaluation Response:") + pprint(eval_object_response) + + # Build data source based on mode + if agent_id_for_server: + data_source: Dict[str, Any] = { + "type": "azure_ai_traces", + "agent_id": agent_id_for_server, + "lookback_hours": lookback_hours, + "max_traces": args.max_traces, + } + else: + assert trace_ids is not None + data_source = { + "type": "azure_ai_traces", + "trace_ids": trace_ids, + "lookback_hours": lookback_hours, + } + + print("\nCreating Eval Run") + run_name = f"agent_trace_eval_{datetime.now().strftime('%Y%m%d_%H%M%S')}" + eval_run_object = client.evals.runs.create( + eval_id=eval_object.id, + name=run_name, + metadata=metadata if metadata else None, + data_source=data_source, # type: ignore + ) + print("Eval Run created") + pprint(eval_run_object) + + print("\nMonitoring Eval Run status...") + while True: + run = client.evals.runs.retrieve(run_id=eval_run_object.id, eval_id=eval_object.id) + print(f"Status: {run.status}") + + if run.status in {"completed", "failed", "canceled"}: + print("\nEval Run finished!") + print("Final Eval Run Response:") + pprint(run) + break + + time.sleep(5) + print("Waiting for eval run to complete...") + + if not args.no_cleanup: + client.evals.delete(eval_id=eval_object.id) + print("Evaluation deleted") + else: + print(f"Skipping cleanup (--no-cleanup). Eval ID: {eval_object.id}") if __name__ == "__main__": diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_audio.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_audio.py index 7583f1e8636e..165bd0f43929 100644 --- a/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_audio.py +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_audio.py @@ -26,13 +26,15 @@ 3) AZURE_AI_MODEL_DEPLOYMENT_NAME_FOR_AUDIO - Required. The name of the model deployment for audio to use for evaluation, recommend to use "gpt-4o-audio-preview" """ -import os import base64 - -from azure.identity import DefaultAzureCredential -from azure.ai.projects import AIProjectClient +import os import time from pprint import pprint + +from dotenv import load_dotenv +from azure.ai.projects import AIProjectClient +from azure.identity import DefaultAzureCredential +from openai.types.eval_create_params import DataSourceConfigCustom from openai.types.evals.create_eval_completions_run_data_source_param import ( CreateEvalCompletionsRunDataSourceParam, SourceFileContent, @@ -41,8 +43,6 @@ InputMessagesTemplateTemplateEvalItem, ) from openai.types.responses import EasyInputMessageParam, ResponseInputAudioParam -from openai.types.eval_create_params import DataSourceConfigCustom -from dotenv import load_dotenv load_dotenv() file_path = os.path.abspath(__file__) @@ -54,7 +54,13 @@ def audio_to_base64(audio_path: str) -> str: - """Read an audio file and return its base64-encoded content.""" + """Read an audio file and return its base64-encoded content. + + :param audio_path: Absolute path to the input audio file. + :type audio_path: str + :return: Base64-encoded audio data. + :rtype: str + """ with open(audio_path, "rb") as f: return base64.b64encode(f.read()).decode("utf-8") @@ -183,7 +189,7 @@ def audio_to_base64(audio_path: str) -> str: while True: run = client.evals.runs.retrieve(run_id=eval_run_response.id, eval_id=eval_object.id) - if run.status == "completed" or run.status == "failed": + if run.status in {"completed", "failed"}: output_items = list(client.evals.runs.output_items.list(run_id=run.id, eval_id=eval_object.id)) pprint(output_items) print(f"Eval Run Report URL: {run.report_url}") diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_audio_model_target.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_audio_model_target.py index 5c3c4ae51f78..75eb7b97f566 100644 --- a/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_audio_model_target.py +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_audio_model_target.py @@ -26,13 +26,15 @@ 3) AZURE_AI_MODEL_DEPLOYMENT_NAME_FOR_AUDIO - Required. The name of the model deployment for audio to use for evaluation, recommend to use "gpt-4o-audio-preview" """ -import os import base64 - -from azure.identity import DefaultAzureCredential -from azure.ai.projects import AIProjectClient +import os import time from pprint import pprint + +from dotenv import load_dotenv +from azure.ai.projects import AIProjectClient +from azure.identity import DefaultAzureCredential +from openai.types.eval_create_params import DataSourceConfigCustom from openai.types.evals.create_eval_completions_run_data_source_param import ( SourceFileContent, SourceFileContentContent, @@ -40,8 +42,6 @@ InputMessagesTemplateTemplateEvalItem, ) from openai.types.responses import EasyInputMessageParam, ResponseInputAudioParam -from openai.types.eval_create_params import DataSourceConfigCustom -from dotenv import load_dotenv load_dotenv() file_path = os.path.abspath(__file__) @@ -53,7 +53,13 @@ def audio_to_base64(audio_path: str) -> str: - """Read an audio file and return its base64-encoded content.""" + """Read an audio file and return its base64-encoded content. + + :param audio_path: Absolute path to the input audio file. + :type audio_path: str + :return: Base64-encoded audio data. + :rtype: str + """ with open(audio_path, "rb") as f: return base64.b64encode(f.read()).decode("utf-8") @@ -188,7 +194,7 @@ def audio_to_base64(audio_path: str) -> str: while True: run = client.evals.runs.retrieve(run_id=eval_run_response.id, eval_id=eval_object.id) - if run.status == "completed" or run.status == "failed": + if run.status in {"completed", "failed"}: output_items = list(client.evals.runs.output_items.list(run_id=run.id, eval_id=eval_object.id)) pprint(output_items) print(f"Eval Run Report URL: {run.report_url}") diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_image_model_target.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_image_model_target.py index 88920549113d..01a22ccfe588 100644 --- a/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_image_model_target.py +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_evaluations_score_model_grader_with_image_model_target.py @@ -23,15 +23,17 @@ 2) AZURE_AI_MODEL_DEPLOYMENT_NAME - Required. The name of the model deployment to use for evaluation. """ -import os import base64 -from PIL import Image +import os +import time from io import BytesIO +from pprint import pprint -from azure.identity import DefaultAzureCredential +from PIL import Image +from dotenv import load_dotenv from azure.ai.projects import AIProjectClient -import time -from pprint import pprint +from azure.identity import DefaultAzureCredential +from openai.types.eval_create_params import DataSourceConfigCustom from openai.types.evals.create_eval_completions_run_data_source_param import ( SourceFileContent, SourceFileContentContent, @@ -40,8 +42,6 @@ InputMessagesTemplateTemplateEvalItemContentInputImage, ) from openai.types.responses import EasyInputMessageParam -from openai.types.eval_create_params import DataSourceConfigCustom -from dotenv import load_dotenv load_dotenv() file_path = os.path.abspath(__file__) @@ -51,8 +51,8 @@ model_deployment_name = os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"] -def image_to_data_uri(image_path: str) -> str: - with Image.open(image_path) as img: +def image_to_data_uri(image_file_path: str) -> str: + with Image.open(image_file_path) as img: buffered = BytesIO() img.save(buffered, format=img.format or "PNG") img_str = base64.b64encode(buffered.getvalue()).decode() @@ -186,7 +186,7 @@ def image_to_data_uri(image_path: str) -> str: while True: run = client.evals.runs.retrieve(run_id=eval_run_response.id, eval_id=eval_object.id) - if run.status == "completed" or run.status == "failed": + if run.status in {"completed", "failed"}: output_items = list(client.evals.runs.output_items.list(run_id=run.id, eval_id=eval_object.id)) pprint(output_items) print(f"Eval Run Report URL: {run.report_url}") diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_conversation_evaluation.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_conversation_evaluation.py new file mode 100644 index 000000000000..375478f59768 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_conversation_evaluation.py @@ -0,0 +1,176 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Given an AIProjectClient, this sample demonstrates how to create and run a + multi-turn conversation evaluation using the synchronous `openai.evals.*` + methods. Multi-turn evaluations assess complete conversations—including + tool-calling exchanges—using conversation-level metrics such as customer + satisfaction, task completion, coherence, and groundedness. + + This sample uses a JSONL dataset where each row contains a ``messages`` + array (and optional ``tool_definitions``). It shows how to: + - Define a ``custom`` data source config with the conversation schema. + - Select conversation-level evaluators with ``{{item.messages}}`` mapping. + - Upload conversation data, create an evaluation, and run it. + - Poll for completion and print results. + +USAGE: + python sample_multiturn_conversation_evaluation.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 - Required. The Azure AI Project endpoint, as found in the overview page of your + Microsoft Foundry project. It has the form: https://.services.ai.azure.com/api/projects/. + 2) FOUNDRY_MODEL_NAME - Required. The name of the model deployment to use for AI-assisted evaluators. +""" + +import os +import time +from pprint import pprint +from dotenv import load_dotenv +from openai.types.evals.create_eval_jsonl_run_data_source_param import ( + CreateEvalJSONLRunDataSourceParam, + SourceFileID, +) +from openai.types.eval_create_params import DataSourceConfigCustom +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import TestingCriterionAzureAIEvaluator + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_deployment_name = os.environ["FOUNDRY_MODEL_NAME"] + +# Path to the multi-turn conversation data file +script_dir = os.path.dirname(os.path.abspath(__file__)) +data_folder = os.environ.get("DATA_FOLDER", os.path.join(script_dir, "data_folder")) +data_file = os.path.join(data_folder, "sample_data_multiturn_conversations.jsonl") + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as client, +): + # Define the data source config for multi-turn conversations. + # The item_schema declares the "messages" array and optional "tool_definitions". + # Set include_sample_schema to False since conversation evaluators use + # {{item.messages}} mapping rather than per-turn sample fields. + data_source_config = DataSourceConfigCustom( + type="custom", + item_schema={ + "type": "object", + "properties": { + "messages": {"type": "array"}, + "tool_definitions": {"type": "array"}, + }, + "required": ["messages"], + }, + include_sample_schema=False, + ) + + # Define conversation-level evaluators. + # All evaluators map to {{item.messages}} to assess the full conversation. + testing_criteria = [ + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="customer_satisfaction", + evaluator_name="builtin.customer_satisfaction", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="task_completion", + evaluator_name="builtin.task_completion", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="conversation_coherence", + evaluator_name="builtin.coherence", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="groundedness", + evaluator_name="builtin.groundedness", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + ] + + print("Creating multi-turn conversation evaluation") + eval_object = client.evals.create( + name="Multi-turn Conversation Evaluation", + data_source_config=data_source_config, + testing_criteria=testing_criteria, + ) + print(f"Evaluation created (id: {eval_object.id})") + + # Upload the conversation dataset + try: + dataset = project_client.datasets.upload_file( + name="multiturn-conversation-data", + version="1", + file_path=data_file, + ) + assert dataset.id is not None, "Dataset upload returned no ID" + data_id: str = dataset.id + print(f"Dataset uploaded (id: {data_id})") + except Exception: + # Dataset already exists — use the existing URI + account = endpoint.split("/")[2].split(".")[0] + project = endpoint.rstrip("/").split("/")[-1] + data_id = f"azureai://accounts/{account}/projects/{project}/data/multiturn-conversation-data/versions/1" + print(f"Using existing dataset (id: {data_id})") + + # Create a run with evaluation_level set to "conversation" + # so evaluators score each conversation as a whole. + eval_run = client.evals.runs.create( + eval_id=eval_object.id, + name="multiturn-conversation-run", + data_source=CreateEvalJSONLRunDataSourceParam( + type="jsonl", + source=SourceFileID( + type="file_id", + id=data_id, + ), + ), + extra_body={"evaluation_level": "conversation"}, + ) + print(f"Evaluation run created (id: {eval_run.id})") + + while True: + run = client.evals.runs.retrieve(run_id=eval_run.id, eval_id=eval_object.id) + if run.status in ("completed", "failed"): + break + print(f"Waiting for eval run to complete... current status: {run.status}") + time.sleep(5) + + if run.status == "completed": + print("\n✓ Evaluation run completed successfully!") + print(f"Result Counts: {run.result_counts}") + + output_items = list(client.evals.runs.output_items.list(run_id=run.id, eval_id=eval_object.id)) + print(f"\nOUTPUT ITEMS (Total: {len(output_items)})") + print(f"{'-'*60}") + pprint(output_items) + print(f"{'-'*60}") + + print(f"\nEval Run Report URL: {run.report_url}") + else: + print(f"\n✗ Evaluation run failed: {run.error}") + + client.evals.delete(eval_id=eval_object.id) + print("Evaluation deleted") diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_conversation_simulation.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_conversation_simulation.py new file mode 100644 index 000000000000..6dd7db32cd9f --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_conversation_simulation.py @@ -0,0 +1,212 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Given an AIProjectClient, this sample demonstrates how to run a conversation + simulation evaluation against a Foundry agent. The service generates multi-turn + conversations by simulating a user interacting with your agent based on seed + scenarios, then evaluates the generated conversations with conversation-level + metrics. + + This is Scenario 4 of multi-turn evaluations: you provide seed scenarios + (each describing a test case), and the service generates full conversations + by replaying simulated user turns against your agent. The generated + conversations are then scored by conversation-level evaluators. + + Key concepts: + - data_source type is "azure_ai_target_completions" with + item_generation_params.type = "conversation_gen_preview" + - num_conversations is per seed scenario (e.g., 2 conversations × 3 scenarios = 6 total) + - max_turns controls the maximum exchanges per conversation + - The seed scenarios source is at the data_source root level + +USAGE: + python sample_multiturn_conversation_simulation.py + + Before running the sample: + + pip install "azure-ai-projects>=2.0.0" python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint. + 2) FOUNDRY_MODEL_NAME - Required. The model deployment name for the simulator + and AI-assisted evaluators. + 3) FOUNDRY_AGENT_NAME - Required. The name of the Foundry agent to simulate against. +""" + +import os +import time +from pprint import pprint +from dotenv import load_dotenv +from openai.types.eval_create_params import DataSourceConfigCustom +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import TestingCriterionAzureAIEvaluator, PromptAgentDefinition + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_deployment_name = os.environ["FOUNDRY_MODEL_NAME"] +agent_name = os.environ.get("FOUNDRY_AGENT_NAME", "") + +# Path to the simulation scenarios data file +script_dir = os.path.dirname(os.path.abspath(__file__)) +data_folder = os.environ.get("DATA_FOLDER", os.path.join(script_dir, "data_folder")) +scenarios_file = os.path.join(data_folder, "sample_data_simulation_scenarios.jsonl") + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as client, +): + # Create (or update) an agent to simulate against + agent = project_client.agents.create_version( + agent_name=agent_name, + definition=PromptAgentDefinition( + model=model_deployment_name, + instructions="You are a helpful customer service agent. Be empathetic and solution-oriented.", + ), + ) + print(f"Agent created (name: {agent.name}, version: {agent.version})") + + # Simulation uses the same "custom" eval group type as dataset evaluation (S1), + # since the generated conversations follow the same messages schema. + data_source_config = DataSourceConfigCustom( + type="custom", + item_schema={ + "type": "object", + "properties": { + "messages": {"type": "array"}, + }, + "required": ["messages"], + }, + include_sample_schema=False, + ) + + # Conversation-level evaluators + testing_criteria = [ + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="customer_satisfaction", + evaluator_name="builtin.customer_satisfaction", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="task_completion", + evaluator_name="builtin.task_completion", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="conversation_coherence", + evaluator_name="builtin.coherence", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="groundedness", + evaluator_name="builtin.groundedness", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + ] + + print("Creating simulation evaluation group") + eval_object = client.evals.create( + name="Multi-turn Conversation Simulation", + data_source_config=data_source_config, + testing_criteria=testing_criteria, + ) + print(f"Evaluation created (id: {eval_object.id})") + + # Upload the simulation scenarios dataset + try: + dataset = project_client.datasets.upload_file( + name="simulation-scenarios", + version="1", + file_path=scenarios_file, + ) + assert dataset.id is not None, "Dataset upload returned no ID" + scenarios_id: str = dataset.id + print(f"Scenarios dataset uploaded (id: {scenarios_id})") + except Exception: + # Dataset already exists — use the existing URI + scenarios_id = f"azureai://accounts/{endpoint.split('/')[2].split('.')[0]}/projects/{endpoint.rstrip('/').split('/')[-1]}/data/simulation-scenarios/versions/1" + print(f"Using existing scenarios dataset (id: {scenarios_id})") + + # Create a simulation run + # - source: the seed scenarios dataset (each row is a test case) + # - target: the agent to simulate against + # - item_generation_params: controls conversation generation + # - num_conversations: conversations to generate per seed scenario + # - max_turns: maximum exchanges per conversation + # - data_mapping: maps JSONL field names to simulation parameters + eval_run = client.evals.runs.create( + eval_id=eval_object.id, + name="conversation-simulation-run", + data_source={ + "type": "azure_ai_target_completions", + "source": { + "type": "file_id", + "id": scenarios_id, + }, + "target": { + "type": "azure_ai_agent", + "name": agent.name, + "version": agent.version, + }, + "item_generation_params": { + "type": "conversation_gen_preview", + "model": model_deployment_name, + "num_conversations": 2, + "max_turns": 5, + "sampling_params": { + "temperature": 0.7, + "top_p": 1.0, + "max_completion_tokens": 800, + }, + "data_mapping": { + "test_case_description": "test_case_description", + "id": "id", + "desired_num_turns": "desired_num_turns", + }, + }, + }, # type: ignore + extra_body={"evaluation_level": "conversation"}, + ) + print(f"Simulation run created (id: {eval_run.id})") + print("Simulation runs are slow (3-8 min). Polling...") + + while True: + run = client.evals.runs.retrieve(run_id=eval_run.id, eval_id=eval_object.id) + if run.status in ("completed", "failed"): + break + print(f"Waiting for simulation to complete... current status: {run.status}") + time.sleep(10) + + if run.status == "completed": + print("\n✓ Simulation run completed successfully!") + print(f"Result Counts: {run.result_counts}") + # With 3 seed scenarios and num_conversations=2, expect 6 total conversations + print(f"Expected: {3 * 2} conversations (3 scenarios × 2 per scenario)") + + output_items = list(client.evals.runs.output_items.list(run_id=run.id, eval_id=eval_object.id)) + print(f"\nOUTPUT ITEMS (Total: {len(output_items)})") + print(f"{'-'*60}") + pprint(output_items) + print(f"{'-'*60}") + + print(f"\nEval Run Report URL: {run.report_url}") + else: + print(f"\n✗ Simulation run failed: {run.error}") + + client.evals.delete(eval_id=eval_object.id) + print("Evaluation deleted") diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_trace_evaluation_agent_filter.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_trace_evaluation_agent_filter.py new file mode 100644 index 000000000000..7a65c9f6b4ba --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_trace_evaluation_agent_filter.py @@ -0,0 +1,183 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Given an AIProjectClient, this sample demonstrates how to evaluate multi-turn + agent conversations by filtering traces from Application Insights using an + agent name/version or agent ID, with optional smart filtering. + + This is Scenario 3 of multi-turn evaluations: instead of providing specific + conversation or trace IDs, you specify an agent identity and a time window. + The service samples traces from App Insights matching that agent and evaluates + the reconstructed conversations. + + Three agent filter forms are supported: + - agent_name + agent_version: Specify the agent by name and version separately. + - agent_id: Specify the agent as a single "name:version" string. + - smart_filtering: Use filter_strategy="smart_filtering" to bias trace + selection toward more interesting conversations. + +USAGE: + python sample_multiturn_trace_evaluation_agent_filter.py + python sample_multiturn_trace_evaluation_agent_filter.py --agent-id "my-agent:1" + python sample_multiturn_trace_evaluation_agent_filter.py --smart-filter + + Before running the sample: + + pip install "azure-ai-projects>=2.0.0" python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint. + 2) FOUNDRY_MODEL_NAME - Required. The model deployment name for AI-assisted evaluators. + 3) FOUNDRY_AGENT_NAME - Required. The name of the agent whose traces to evaluate. + 4) FOUNDRY_AGENT_VERSION - Optional. The agent version. If not set, latest is used. +""" + +import argparse +import os +import time +from pprint import pprint +from dotenv import load_dotenv +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import TestingCriterionAzureAIEvaluator + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_deployment_name = os.environ["FOUNDRY_MODEL_NAME"] +agent_name = os.environ["FOUNDRY_AGENT_NAME"] +agent_version = os.environ.get("FOUNDRY_AGENT_VERSION", "") + + +def main() -> None: + parser = argparse.ArgumentParser(description="Evaluate agent traces using agent filter.") + parser.add_argument("--agent-id", default=None, help='Agent ID in "name:version" format') + parser.add_argument("--smart-filter", action="store_true", help="Use smart_filtering strategy") + parser.add_argument("--max-traces", type=int, default=5, help="Max traces to evaluate (default: 5)") + parser.add_argument("--lookback-hours", type=int, default=24, help="Hours to look back (default: 24)") + args = parser.parse_args() + + with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as client, + ): + # Eval group for trace-based evaluations + data_source_config = { + "type": "azure_ai_source", + "scenario": "traces", + } + + testing_criteria = [ + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="customer_satisfaction", + evaluator_name="builtin.customer_satisfaction", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="task_completion", + evaluator_name="builtin.task_completion", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="conversation_coherence", + evaluator_name="builtin.coherence", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="groundedness", + evaluator_name="builtin.groundedness", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + ] + + print("Creating trace-based evaluation group") + eval_object = client.evals.create( + name="Multi-turn Trace Evaluation (Agent Filter)", + data_source_config=data_source_config, # type: ignore + testing_criteria=testing_criteria, + ) + print(f"Evaluation created (id: {eval_object.id})") + + # Compute time window in unix seconds + # Pad end_time by +600s (10 min) to avoid ingestion-delay edge exclusion + now_unix = int(time.time()) + end_time = now_unix + 600 + start_time = now_unix - (args.lookback_hours * 3600) + + # Build trace_source based on mode + trace_source: dict = { + "type": "agent_filter", + "start_time": start_time, + "end_time": end_time, + "max_traces": args.max_traces, + } + + if args.agent_id: + # agent_id form: single "name:version" string + trace_source["agent_id"] = args.agent_id + print(f"Using agent_id filter: {args.agent_id}") + else: + # agent_name + agent_version form + trace_source["agent_name"] = agent_name + if agent_version: + trace_source["agent_version"] = agent_version + print(f"Using agent filter: {agent_name} v{agent_version or '(latest)'}") + + if args.smart_filter: + trace_source["filter_strategy"] = "smart_filtering" + print("Filter strategy: smart_filtering") + + data_source = { + "type": "azure_ai_trace_data_source_preview", + "trace_source": trace_source, + } + + eval_run = client.evals.runs.create( + eval_id=eval_object.id, + name="multiturn-agent-filter-run", + data_source=data_source, # type: ignore + extra_body={"evaluation_level": "conversation"}, + ) + print(f"Evaluation run created (id: {eval_run.id})") + + while True: + run = client.evals.runs.retrieve(run_id=eval_run.id, eval_id=eval_object.id) + if run.status in ("completed", "failed"): + break + print(f"Waiting for eval run to complete... current status: {run.status}") + time.sleep(5) + + if run.status == "completed": + print("\n✓ Evaluation run completed successfully!") + print(f"Result Counts: {run.result_counts}") + + output_items = list(client.evals.runs.output_items.list(run_id=run.id, eval_id=eval_object.id)) + print(f"\nOUTPUT ITEMS (Total: {len(output_items)})") + print(f"{'-'*60}") + pprint(output_items) + print(f"{'-'*60}") + + print(f"\nEval Run Report URL: {run.report_url}") + else: + print(f"\n✗ Evaluation run failed: {run.error}") + + client.evals.delete(eval_id=eval_object.id) + print("Evaluation deleted") + + +if __name__ == "__main__": + main() diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_trace_evaluation_by_id.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_trace_evaluation_by_id.py new file mode 100644 index 000000000000..f8117be7ae3b --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_multiturn_trace_evaluation_by_id.py @@ -0,0 +1,166 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Given an AIProjectClient, this sample demonstrates how to evaluate multi-turn + conversations captured as agent traces in Application Insights, using specific + conversation IDs or trace IDs to select which conversations to evaluate. + + This is Scenario 2 of multi-turn evaluations: you provide known conversation + or trace identifiers, and the service reconstructs the messages from App Insights + traces, then runs conversation-level evaluators against them. + + Two modes are supported: + - conversation_id_source: Provide Foundry conversation IDs. + - trace_id_source: Provide W3C trace IDs (operation_Id from App Insights). + +USAGE: + python sample_multiturn_trace_evaluation_by_id.py + + Before running the sample: + + pip install "azure-ai-projects>=2.0.0" python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint. + 2) FOUNDRY_MODEL_NAME - Required. The model deployment name for AI-assisted evaluators. + 3) FOUNDRY_CONVERSATION_IDS - Required (for conversation_id mode). Comma-separated + Foundry conversation IDs to evaluate. + Example: "conv_abc123,conv_def456,conv_ghi789" + 4) FOUNDRY_TRACE_IDS - Optional (for trace_id mode). Comma-separated W3C trace IDs. + If set, overrides conversation IDs. +""" + +import os +import time +from pprint import pprint +from dotenv import load_dotenv +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import TestingCriterionAzureAIEvaluator + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_deployment_name = os.environ["FOUNDRY_MODEL_NAME"] + +# Choose one: conversation IDs or trace IDs +conversation_ids_str = os.environ.get("FOUNDRY_CONVERSATION_IDS", "") +trace_ids_str = os.environ.get("FOUNDRY_TRACE_IDS", "") + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as client, +): + # Eval group for trace-based evaluations uses azure_ai_source with scenario "traces" + data_source_config = { + "type": "azure_ai_source", + "scenario": "traces", + } + + # Conversation-level evaluators for trace data + testing_criteria = [ + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="customer_satisfaction", + evaluator_name="builtin.customer_satisfaction", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="task_completion", + evaluator_name="builtin.task_completion", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="conversation_coherence", + evaluator_name="builtin.coherence", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name="groundedness", + evaluator_name="builtin.groundedness", + initialization_parameters={"model": model_deployment_name}, + data_mapping={"messages": "{{item.messages}}"}, + ), + ] + + print("Creating trace-based evaluation group") + eval_object = client.evals.create( + name="Multi-turn Trace Evaluation (by ID)", + data_source_config=data_source_config, # type: ignore + testing_criteria=testing_criteria, + ) + print(f"Evaluation created (id: {eval_object.id})") + + # Build the data source based on which IDs are provided + if trace_ids_str: + # Trace ID mode — provide W3C trace IDs (operation_Id from App Insights) + trace_ids = [tid.strip() for tid in trace_ids_str.split(",") if tid.strip()] + print(f"Using {len(trace_ids)} trace IDs") + data_source = { + "type": "azure_ai_trace_data_source_preview", + "trace_source": { + "type": "trace_id_source", + "trace_ids": trace_ids, + }, + } + else: + # Conversation ID mode — provide Foundry conversation IDs + conversation_ids = [cid.strip() for cid in conversation_ids_str.split(",") if cid.strip()] + if not conversation_ids: + raise ValueError( + "Set FOUNDRY_CONVERSATION_IDS or FOUNDRY_TRACE_IDS. " + "These are IDs from prior agent interactions captured in App Insights." + ) + print(f"Using {len(conversation_ids)} conversation IDs") + data_source = { + "type": "azure_ai_trace_data_source_preview", + "trace_source": { + "type": "conversation_id_source", + "conversation_ids": conversation_ids, + }, + } + + # Create run with evaluation_level = "conversation" + eval_run = client.evals.runs.create( + eval_id=eval_object.id, + name="multiturn-trace-by-id-run", + data_source=data_source, # type: ignore + extra_body={"evaluation_level": "conversation"}, + ) + print(f"Evaluation run created (id: {eval_run.id})") + + while True: + run = client.evals.runs.retrieve(run_id=eval_run.id, eval_id=eval_object.id) + if run.status in ("completed", "failed"): + break + print(f"Waiting for eval run to complete... current status: {run.status}") + time.sleep(5) + + if run.status == "completed": + print("\n✓ Evaluation run completed successfully!") + print(f"Result Counts: {run.result_counts}") + + output_items = list(client.evals.runs.output_items.list(run_id=run.id, eval_id=eval_object.id)) + print(f"\nOUTPUT ITEMS (Total: {len(output_items)})") + print(f"{'-'*60}") + pprint(output_items) + print(f"{'-'*60}") + + print(f"\nEval Run Report URL: {run.report_url}") + else: + print(f"\n✗ Evaluation run failed: {run.error}") + + client.evals.delete(eval_id=eval_object.id) + print("Evaluation deleted") diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_redteam_evaluations.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_redteam_evaluations.py index 82cd63dfe664..a88d71125b12 100644 --- a/sdk/ai/azure-ai-projects/samples/evaluations/sample_redteam_evaluations.py +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_redteam_evaluations.py @@ -95,7 +95,7 @@ def main() -> None: # pylint: disable=too-many-statements description="Taxonomy for red teaming evaluation", taxonomy_input=agent_taxonomy_input ) - taxonomy = project_client.beta.evaluation_taxonomies.create(name=agent_name, body=eval_taxonomy_input) + taxonomy = project_client.beta.evaluation_taxonomies.create(name=agent_name, taxonomy=eval_taxonomy_input) taxonomy_path = os.path.join(tempfile.gettempdir(), f"taxonomy_{agent_name}.json") with open(taxonomy_path, "w", encoding="utf-8") as f: f.write(json.dumps(_to_json_primitive(taxonomy), indent=2)) diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_all_sources.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_all_sources.py new file mode 100644 index 000000000000..5e29496f0eae --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_all_sources.py @@ -0,0 +1,250 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + End-to-end scenario showing every rubric evaluator generation source type + in a single sample file. There are four source types: + + 1. `Prompt` - an inline natural-language description of the application. + 2. `Agent` - references an agent registered in the Foundry project. + 3. `Dataset` - references an uploaded dataset (name + version). + 4. `traces` - Application Insights conversation traces for an agent, + within a time window. + + The first three source types can be combined into a single generation job + to produce a richer rubric. The `traces` source type requires a companion + source (the service rejects `traces`-only source arrays), so this sample + submits it as a separate, second job paired with an `Agent` companion. + + Optional environment variables let you skip variants where you don't have + a suitable agent, dataset, or recent traces in the target Foundry project. + +USAGE: + python sample_rubric_evaluator_generation_all_sources.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found + in the overview page of your Microsoft Foundry project. + 2) FOUNDRY_MODEL_NAME - Required. The name of the model the generation job + will use (e.g. `gpt-4o`, `gpt-4.1`). The generation runs inline server + side, so no deployment in your project is required. + 3) FOUNDRY_AGENT_NAME - Optional. Name of an agent registered in the project. + Enables the `Agent` source and the `traces`-source job. + 4) FOUNDRY_REFERENCE_DATASET_NAME - Optional. Name of an uploaded dataset. + Enables the `Dataset` source. + 5) FOUNDRY_REFERENCE_DATASET_VERSION - Optional. Version of the uploaded dataset. + Enables the `Dataset` source. + 6) FOUNDRY_TRACES_WINDOW_DAYS - Optional. Look-back window in days for the + `traces` source. Defaults to 7. + 7) POLL_INTERVAL_SECONDS - Optional. Number of seconds to sleep between + generation job status polls. Defaults to 10. +""" + +import os +import time +import uuid +from datetime import datetime, timezone +from typing import Any, Dict, List, cast + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import JobStatus + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_name = os.environ["FOUNDRY_MODEL_NAME"] +agent_name = os.environ.get("FOUNDRY_AGENT_NAME") +dataset_name = os.environ.get("FOUNDRY_REFERENCE_DATASET_NAME") +dataset_version = os.environ.get("FOUNDRY_REFERENCE_DATASET_VERSION") +traces_window_days = int(os.environ.get("FOUNDRY_TRACES_WINDOW_DAYS", "7")) +poll_interval_seconds = int(os.environ.get("POLL_INTERVAL_SECONDS", "10")) + +# Unique per-run suffix so repeated runs do not collide on evaluator name. +ts = datetime.now(tz=timezone.utc).strftime("%Y%m%d%H%M%S") +short = uuid.uuid4().hex[:6] +multi_name = f"multi-source-{ts}-{short}" +traces_name = f"traces-source-{ts}-{short}" + +TERMINAL_STATUSES = {JobStatus.SUCCEEDED, JobStatus.FAILED, JobStatus.CANCELLED} + +multi_evaluator_version = "" +traces_evaluator_version = "" + +with ( + DefaultAzureCredential() as credential, + # `allow_preview` and `api_version` are required for the evaluator + # generation endpoints in this preview. + AIProjectClient( + endpoint=endpoint, + credential=credential, + allow_preview=True, + api_version="2025-11-15-preview", + ) as project_client, +): + + # ------------------------------------------------------------------ + # 1. Combined Prompt + Agent + Dataset generation job. + # ------------------------------------------------------------------ + print("Build sources for the combined generation job:") + multi_sources: List[Dict[str, Any]] = [ + { + "type": "Prompt", + "description": "Inline application overview.", + "prompt": ( + "You are evaluating a customer-support assistant that helps users " + "manage their accounts, troubleshoot issues, and place orders. The " + "assistant uses tools for account lookup, password reset, and order " + "creation. It must confirm intent before performing destructive " + "actions and maintain a patient, professional tone." + ), + } + ] + if agent_name: + print(f" Including Agent source: `{agent_name}`.") + multi_sources.append( + { + "type": "Agent", + "description": "Agent metadata enriches the rubric with tool and instruction signals.", + "agent_name": agent_name, + } + ) + else: + print(" Skipping Agent source (FOUNDRY_AGENT_NAME not set).") + + if dataset_name and dataset_version: + print(f" Including Dataset source: `{dataset_name}` v`{dataset_version}`.") + multi_sources.append( + { + "type": "Dataset", + "description": "Reference examples ground dimensions in real data.", + "name": dataset_name, + "version": dataset_version, + } + ) + else: + print(" Skipping Dataset source (FOUNDRY_REFERENCE_DATASET_NAME / _VERSION not set).") + + print(f"Create combined generation job for evaluator `{multi_name}`.") + multi_job = project_client.beta.evaluators.create_generation_job( + job={ + "model": model_name, + "name": "Multi-source generation", + "evaluator_name": multi_name, + "evaluator_display_name": "Customer Support Quality (multi-source)", + "evaluator_description": "Generated from prompt, agent, and dataset signals.", + "sources": multi_sources, + }, + operation_id=f"rubric-multi-{short}", + ) + print(f"Created generation job `{multi_job.id}` (status: `{cast(JobStatus, multi_job.status).value}`).") + + print(f"Poll job `{multi_job.id}` until it reaches a terminal state.", end="", flush=True) + while multi_job.status not in TERMINAL_STATUSES: + time.sleep(poll_interval_seconds) + multi_job = project_client.beta.evaluators.get_generation_job(multi_job.id) + print(".", end="", flush=True) + print() + print(f"Final job status: `{cast(JobStatus, multi_job.status).value}`.") + + if multi_job.status != JobStatus.SUCCEEDED: + message = multi_job.error.message if multi_job.error is not None else "" + print( + f"Combined job `{multi_job.id}` ended with status " + f"`{cast(JobStatus, multi_job.status).value}`: {message}" + ) + else: + evaluator = multi_job.result + multi_evaluator_version = evaluator.version or "" + print(f"Generated evaluator: name=`{evaluator.name}` version=`{evaluator.version}`.") + print(f"Categories: {[c.value for c in evaluator.categories]}") + print(f"Dimensions ({len(evaluator.definition.dimensions)}):") + for dim in evaluator.definition.dimensions: + marker = " [ALWAYS-ON]" if dim.always_applicable else "" + print(f" - {dim.id} (weight={dim.weight}){marker}") + + # ------------------------------------------------------------------ + # 2. Separate `traces` + Agent companion generation job. + # ------------------------------------------------------------------ + # The traces source requires a companion source because the service + # rejects sources arrays consisting only of traces. The Agent source + # is the typical companion. + if not agent_name: + print("Skip traces job: requires FOUNDRY_AGENT_NAME for both the traces source and companion.") + else: + print(f"Create traces-source generation job for evaluator `{traces_name}`.") + print(f" agent=`{agent_name}` look-back window: {traces_window_days} days") + now = int(time.time()) + start_time = now - traces_window_days * 24 * 3600 + end_time = now + 600 # small padding for clock skew + + traces_job = project_client.beta.evaluators.create_generation_job( + job={ + "model": model_name, + "name": "Traces-source generation", + "evaluator_name": traces_name, + "evaluator_display_name": "Customer Support Quality (from traces)", + "evaluator_description": "Generated from real Application Insights conversation traces.", + "sources": [ + { + "type": "traces", + "description": "Application Insights conversation traces for the agent.", + "agent_name": agent_name, + "start_time": start_time, + "end_time": end_time, + }, + { + "type": "Agent", + "description": "Companion source (service rejects traces-only).", + "agent_name": agent_name, + }, + ], + }, + operation_id=f"rubric-traces-{short}", + ) + print(f"Created generation job `{traces_job.id}` (status: `{cast(JobStatus, traces_job.status).value}`).") + + print(f"Poll job `{traces_job.id}` until it reaches a terminal state.", end="", flush=True) + while traces_job.status not in TERMINAL_STATUSES: + time.sleep(poll_interval_seconds) + traces_job = project_client.beta.evaluators.get_generation_job(traces_job.id) + print(".", end="", flush=True) + print() + print(f"Final job status: `{cast(JobStatus, traces_job.status).value}`.") + + if traces_job.status != JobStatus.SUCCEEDED: + message = traces_job.error.message if traces_job.error is not None else "" + print( + f"Traces job `{traces_job.id}` ended with status " + f"`{cast(JobStatus, traces_job.status).value}`: {message}" + ) + else: + evaluator = traces_job.result + traces_evaluator_version = evaluator.version or "" + print(f"Generated evaluator: name=`{evaluator.name}` version=`{evaluator.version}`.") + print(f"Categories: {[c.value for c in evaluator.categories]}") + print(f"Dimensions ({len(evaluator.definition.dimensions)}):") + for dim in evaluator.definition.dimensions: + marker = " [ALWAYS-ON]" if dim.always_applicable else "" + print(f" - {dim.id} (weight={dim.weight}){marker}") + + # ------------------------------------------------------------------ + # 3. Clean up. + # ------------------------------------------------------------------ + # `delete_version` cascades to delete the generation job record as well. + if multi_evaluator_version: + print(f"Delete evaluator `{multi_name}` version `{multi_evaluator_version}`.") + project_client.beta.evaluators.delete_version(name=multi_name, version=multi_evaluator_version) + if traces_evaluator_version: + print(f"Delete evaluator `{traces_name}` version `{traces_evaluator_version}`.") + project_client.beta.evaluators.delete_version(name=traces_name, version=traces_evaluator_version) diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_basic.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_basic.py new file mode 100644 index 000000000000..220d001400c0 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_basic.py @@ -0,0 +1,271 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + End-to-end scenario showing rubric evaluator generation from a single + `Prompt` source, followed by an OpenAI evaluation run that uses the + generated evaluator. The sample: + + 1. Creates an `EvaluatorGenerationJob` whose only source is an inline + natural-language description of the application's purpose, capabilities, + and tools. The service synthesizes a rubric tailored to that application. + 2. Polls the generation job to completion and resolves the generated + `EvaluatorVersion`. + 3. Creates an OpenAI evaluation (`client.evals.create`) referencing the + generated evaluator as a testing criterion. + 4. Runs the evaluation against inline JSONL sample data. + 5. Cleans up the evaluation and the evaluator version. Deleting the + evaluator version cascades to delete the generation job record. + + Other source types - `Agent`, `Dataset`, and `traces` - can be used in + place of (or alongside) the prompt source. See + `sample_rubric_evaluator_generation_all_sources.py` for examples of each. + +USAGE: + python sample_rubric_evaluator_generation_basic.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found + in the overview page of your Microsoft Foundry project. + 2) FOUNDRY_MODEL_NAME - Required. The name of the model used by both the + generation job and the eval run's LLM judge (e.g. `gpt-4o`, `gpt-4.1`). + The generation runs inline server side (no deployment required), but the + eval run's grader does require a model deployment in your project. + 3) POLL_INTERVAL_SECONDS - Optional. Number of seconds to sleep between status + polls for both the generation job and the evaluation run. Defaults to 10. +""" + +import os +import time +import uuid +from datetime import datetime, timezone +from typing import cast + +from dotenv import load_dotenv +from openai.types.eval_create_params import DataSourceConfigCustom +from openai.types.evals.create_eval_jsonl_run_data_source_param import ( + CreateEvalJSONLRunDataSourceParam, + SourceFileContent, + SourceFileContentContent, +) + +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import JobStatus, TestingCriterionAzureAIEvaluator + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_name = os.environ["FOUNDRY_MODEL_NAME"] +poll_interval_seconds = int(os.environ.get("POLL_INTERVAL_SECONDS", "10")) + +# Unique per-run name so repeated runs do not collide. +ts = datetime.now(tz=timezone.utc).strftime("%Y%m%d%H%M%S") +short = uuid.uuid4().hex[:6] +evaluator_name = f"reservation-quality-generated-{ts}-{short}" + +TERMINAL_STATUSES = {JobStatus.SUCCEEDED, JobStatus.FAILED, JobStatus.CANCELLED} +TERMINAL_RUN_STATUSES = {"completed", "failed", "canceled"} + +with ( + DefaultAzureCredential() as credential, + # `allow_preview` and `api_version` are required for the evaluator + # generation endpoints in this preview. + AIProjectClient( + endpoint=endpoint, + credential=credential, + allow_preview=True, + api_version="2025-11-15-preview", + ) as project_client, + project_client.get_openai_client() as openai_client, +): + + # ------------------------------------------------------------------ + # 1. Generate an evaluator from a single `Prompt` source. + # ------------------------------------------------------------------ + # The body is sent as a plain dict to match the wire shape expected by + # the 2025-11-15-preview API. + print(f"Create generation job for evaluator `{evaluator_name}`.") + job = project_client.beta.evaluators.create_generation_job( + job={ + "model": model_name, + "name": "Reservation Quality (Generated)", + "evaluator_name": evaluator_name, + "evaluator_display_name": "Reservation Quality (Generated)", + "evaluator_description": ( + "Quality evaluator generated from a prompt describing a " "restaurant reservation assistant." + ), + "sources": [ + { + "type": "Prompt", + "description": "Application overview - purpose, capabilities, and tools.", + "prompt": ( + "You are evaluating a restaurant reservation assistant. The assistant helps " + "users create, modify, and cancel reservations at participating restaurants. " + "It can:\n" + " - Search for restaurants by name, cuisine, or neighborhood.\n" + " - Check table availability for a requested date, time, and party size.\n" + " - Create, update, and cancel reservations on behalf of the user.\n" + " - Send SMS or email confirmations through a notifications tool.\n" + "It must always confirm the user's intent before committing changes, " + "ask follow-up questions when details are missing, and maintain a polite " + "restaurant-host tone." + ), + } + ], + }, + # `operation_id` makes the call idempotent - re-submitting the same id + # returns the existing job instead of creating a duplicate. + operation_id=f"rubric-eval-basic-{short}", + ) + print(f"Created generation job `{job.id}` (status: `{cast(JobStatus, job.status).value}`).") + + print(f"Poll job `{job.id}` until it reaches a terminal state.", end="", flush=True) + while job.status not in TERMINAL_STATUSES: + time.sleep(poll_interval_seconds) + job = project_client.beta.evaluators.get_generation_job(job.id) + print(".", end="", flush=True) + print() + print(f"Final job status: `{cast(JobStatus, job.status).value}`.") + + if job.status != JobStatus.SUCCEEDED: + message = job.error.message if job.error is not None else "" + raise RuntimeError( + f"Generation job `{job.id}` ended with status `{cast(JobStatus, job.status).value}`: {message}" + ) + + if job.usage is not None: + print(f"Token usage: {job.usage}") + + # On success, the evaluator is automatically saved as version 1. + evaluator = job.result + print(f"Generated evaluator: name=`{evaluator.name}` version=`{evaluator.version}`.") + print(f"Categories: {[c.value for c in evaluator.categories]}") + print(f"Pass threshold: {evaluator.definition.pass_threshold}") + print(f"Dimensions ({len(evaluator.definition.dimensions)}):") + for dim in evaluator.definition.dimensions: + # Quality evaluators always include a non-editable `general_quality` + # residual dimension with always_applicable=True. + marker = " [ALWAYS-ON]" if dim.always_applicable else "" + print(f" - {dim.id} (weight={dim.weight}){marker}: {dim.description[:120]}") + + # ------------------------------------------------------------------ + # 2. Create an OpenAI evaluation that uses the generated evaluator. + # ------------------------------------------------------------------ + data_source_config = DataSourceConfigCustom( + type="custom", + item_schema={ + "type": "object", + "properties": { + "query": {"type": "string"}, + "response": {"type": "string"}, + }, + "required": ["query", "response"], + }, + include_sample_schema=True, + ) + + testing_criteria = [ + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name=evaluator.name, + evaluator_name=evaluator.name, + initialization_parameters={"deployment_name": model_name}, + data_mapping={ + "query": "{{item.query}}", + "response": "{{item.response}}", + }, + ) + ] + + print("Create the evaluation.") + eval_object = openai_client.evals.create( + name=f"{evaluator.name}-eval", + data_source_config=data_source_config, + testing_criteria=testing_criteria, + ) + print(f"Evaluation created (id: {eval_object.id}).") + + # ------------------------------------------------------------------ + # 3. Run the evaluation against inline JSONL sample data. + # ------------------------------------------------------------------ + print(f"Create an evaluation run for eval `{eval_object.id}`.") + eval_run = openai_client.evals.runs.create( + eval_id=eval_object.id, + name=f"{evaluator.name}-run", + metadata={"sample": "rubric_evaluator_generation_basic"}, + data_source=CreateEvalJSONLRunDataSourceParam( + type="jsonl", + source=SourceFileContent( + type="file_content", + content=[ + SourceFileContentContent( + item={ + "query": "Book a table for 4 tomorrow at 7 PM.", + "response": ( + "Booked - table for 4 tomorrow at 7:00 PM. A confirmation " "SMS is on its way." + ), + } + ), + SourceFileContentContent( + item={ + "query": "Cancel my reservation for Friday night.", + "response": "Sure.", + } + ), + ], + ), + ), + ) + print(f"Evaluation run created (id: {eval_run.id}).") + + print(f"Poll run `{eval_run.id}` until it reaches a terminal state.", end="", flush=True) + while eval_run.status not in TERMINAL_RUN_STATUSES: + time.sleep(poll_interval_seconds) + eval_run = openai_client.evals.runs.retrieve(run_id=eval_run.id, eval_id=eval_object.id) + print(".", end="", flush=True) + print() + print(f"Final eval run status: `{eval_run.status}`.") + + if eval_run.status == "completed": + print(f"Result counts: {eval_run.result_counts}") + if eval_run.report_url: + print(f"Eval run report URL: {eval_run.report_url}") + output_items = list(openai_client.evals.runs.output_items.list(run_id=eval_run.id, eval_id=eval_object.id)) + print(f"Output items (total: {len(output_items)}):") + for idx, item in enumerate(output_items, start=1): + results = getattr(item, "results", None) or [] + parts = [] + for r in results: + # Result entries are returned either as typed objects (Azure AI + # evaluators) or as plain dicts (some OpenAI-native evaluators). + if isinstance(r, dict): + name = r.get("name", "?") + score = r.get("score", "n/a") + passed = r.get("passed", "n/a") + else: + name = getattr(r, "name", "?") + score = getattr(r, "score", "n/a") + passed = getattr(r, "passed", "n/a") + parts.append(f"{name}={score} ({passed})") + print(f" item {idx}: status={item.status} | {', '.join(parts)}") + else: + print("Evaluation run did not complete successfully.") + + # ------------------------------------------------------------------ + # 4. Clean up. + # ------------------------------------------------------------------ + print(f"Delete evaluation `{eval_object.id}`.") + openai_client.evals.delete(eval_id=eval_object.id) + + print(f"Delete evaluator `{evaluator.name}` version `{evaluator.version}`.") + # `delete_version` cascades to delete the generation job record as well. + project_client.beta.evaluators.delete_version(name=evaluator.name, version=evaluator.version) diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_iterate.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_iterate.py new file mode 100644 index 000000000000..3c7c05afba2f --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_iterate.py @@ -0,0 +1,218 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + End-to-end scenario showing the human-in-the-loop iteration workflow for + rubric evaluators. This is the typical pattern when the first generated + rubric is a good starting point but a domain expert wants to tune the + weighting or add custom dimensions. The sample: + + 1. Generates v1 of an evaluator from a single `Prompt` source. + 2. Inspects the dimensions the service produced. + 3. Edits the dimensions locally - boosts the highest-weight editable + dimension to 10, drops the lowest-weight editable dimension, and + adds a new custom dimension. The non-editable `general_quality` + ALWAYS-ON dimension is preserved verbatim. + 4. Saves the edited definition as v2 with `create_version`. + 5. Calls `list_versions` to enumerate v1 and v2. + 6. Cleans up by deleting both versions. + +USAGE: + python sample_rubric_evaluator_generation_iterate.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found + in the overview page of your Microsoft Foundry project. + 2) FOUNDRY_MODEL_NAME - Required. The name of the model the generation job + will use (e.g. `gpt-4o`, `gpt-4.1`). The generation runs inline server + side, so no deployment in your project is required. + 3) POLL_INTERVAL_SECONDS - Optional. Number of seconds to sleep between status + polls for the generation job. Defaults to 10. +""" + +import os +import time +import uuid +from datetime import datetime, timezone +from typing import cast + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import EvaluatorDefinitionType, JobStatus + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_name = os.environ["FOUNDRY_MODEL_NAME"] +poll_interval_seconds = int(os.environ.get("POLL_INTERVAL_SECONDS", "10")) + +# Unique per-run name so repeated runs do not collide. +ts = datetime.now(tz=timezone.utc).strftime("%Y%m%d%H%M%S") +short = uuid.uuid4().hex[:6] +evaluator_name = f"reservation-quality-iterate-{ts}-{short}" + +TERMINAL_STATUSES = {JobStatus.SUCCEEDED, JobStatus.FAILED, JobStatus.CANCELLED} + +with ( + DefaultAzureCredential() as credential, + # `allow_preview` and `api_version` are required for the evaluator + # generation endpoints in this preview. + AIProjectClient( + endpoint=endpoint, + credential=credential, + allow_preview=True, + api_version="2025-11-15-preview", + ) as project_client, +): + + # ------------------------------------------------------------------ + # 1. Generate v1 of the evaluator from a single `Prompt` source. + # ------------------------------------------------------------------ + print(f"Create generation job for evaluator `{evaluator_name}` (v1).") + job = project_client.beta.evaluators.create_generation_job( + job={ + "model": model_name, + "name": "Reservation Quality (iterate)", + "evaluator_name": evaluator_name, + "evaluator_display_name": "Reservation Quality (iterate)", + "evaluator_description": "Starting point for human-in-the-loop iteration.", + "sources": [ + { + "type": "Prompt", + "description": "Inline application overview.", + "prompt": ( + "You are evaluating a restaurant reservation assistant that creates, " + "modifies, and cancels reservations. It uses tools for restaurant " + "lookup, availability checking, and notifications. It must confirm " + "user intent before committing changes." + ), + } + ], + }, + operation_id=f"rubric-iterate-{short}", + ) + print(f"Created generation job `{job.id}` (status: `{cast(JobStatus, job.status).value}`).") + + print(f"Poll job `{job.id}` until it reaches a terminal state.", end="", flush=True) + while job.status not in TERMINAL_STATUSES: + time.sleep(poll_interval_seconds) + job = project_client.beta.evaluators.get_generation_job(job.id) + print(".", end="", flush=True) + print() + print(f"Final job status: `{cast(JobStatus, job.status).value}`.") + + if job.status != JobStatus.SUCCEEDED: + message = job.error.message if job.error is not None else "" + raise RuntimeError( + f"Generation job `{job.id}` ended with status `{cast(JobStatus, job.status).value}`: {message}" + ) + + v1 = job.result + print(f"v1 created: version=`{v1.version}`.") + print(f"v1 dimensions ({len(v1.definition.dimensions)}):") + for dim in v1.definition.dimensions: + marker = " [ALWAYS-ON]" if dim.always_applicable else "" + print(f" - {dim.id} (weight={dim.weight}){marker}") + + # ------------------------------------------------------------------ + # 2. Edit dimensions locally. + # ------------------------------------------------------------------ + # Domain-expert edits: + # * Always preserve the ALWAYS-ON `general_quality` residual dimension + # exactly as-is (id, weight, description, always_applicable). + # * Boost the most important editable dimension to weight 10. + # * Drop the lowest-weight editable dimension as redundant. + # * Add a new custom dimension specific to this assistant. + print("Apply human edits:") + editable = [d for d in v1.definition.dimensions if not d.always_applicable] + always_on = [d for d in v1.definition.dimensions if d.always_applicable] + + edited_dimensions = [] + if editable: + top = max(editable, key=lambda d: d.weight) + lowest = min(editable, key=lambda d: d.weight) + print(f" Boost `{top.id}` weight {top.weight} -> 10.") + print(f" Drop `{lowest.id}` (weight={lowest.weight}).") + for dim in editable: + if dim.id == lowest.id: + continue + edited_dimensions.append( + { + "id": dim.id, + "description": dim.description, + "weight": 10 if dim.id == top.id else dim.weight, + } + ) + + new_dimension = { + "id": "wait_time_expectations_set", + "description": ( + "Sets clear expectations about wait time, table readiness, or confirmation " + "delivery so the user knows what happens next." + ), + "weight": 4, + } + edited_dimensions.append(new_dimension) + print(f" Add new dimension `{new_dimension['id']}` (weight={new_dimension['weight']}).") + + # Preserve every ALWAYS-ON dimension verbatim. These are non-editable. + for dim in always_on: + print(f" Preserve ALWAYS-ON dimension `{dim.id}` (weight={dim.weight}) verbatim.") + edited_dimensions.append( + { + "id": dim.id, + "description": dim.description, + "weight": dim.weight, + "always_applicable": True, + } + ) + + # ------------------------------------------------------------------ + # 3. Save the edited definition as v2. + # ------------------------------------------------------------------ + print(f"Save edited definition as v2 of `{evaluator_name}`.") + v2 = project_client.beta.evaluators.create_version( + name=evaluator_name, + evaluator_version={ + "name": evaluator_name, + "categories": [c.value for c in v1.categories], + "display_name": v1.display_name, + "description": (v1.description or "") + " (edited)", + "definition": { + "type": EvaluatorDefinitionType.RUBRIC, + "dimensions": edited_dimensions, + "pass_threshold": v1.definition.pass_threshold or 0.6, + }, + }, + ) + print(f"v2 created: version=`{v2.version}`.") + print(f"v2 dimensions ({len(v2.definition.dimensions)}):") + for dim in v2.definition.dimensions: + marker = " [ALWAYS-ON]" if dim.always_applicable else "" + print(f" - {dim.id} (weight={dim.weight}){marker}") + + # ------------------------------------------------------------------ + # 4. List all versions of the evaluator. + # ------------------------------------------------------------------ + print(f"List all versions for evaluator `{evaluator_name}`:") + for ver in project_client.beta.evaluators.list_versions(name=evaluator_name): + print(f" - version=`{ver.version}` dimensions={len(ver.definition.dimensions)}") + + # ------------------------------------------------------------------ + # 5. Clean up. + # ------------------------------------------------------------------ + # Delete the highest version first to avoid any version-ordering issues. + for version in (v2.version, v1.version): + if version: + print(f"Delete evaluator `{evaluator_name}` version `{version}`.") + project_client.beta.evaluators.delete_version(name=evaluator_name, version=version) diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_lifecycle.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_lifecycle.py new file mode 100644 index 000000000000..392eb3735aae --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_generation_lifecycle.py @@ -0,0 +1,172 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + End-to-end scenario showing the full lifecycle of rubric evaluator + generation jobs. The sample exercises: + + * `create_generation_job` with `operation_id` for idempotent re-submits. + * `get_generation_job` to poll a single job to completion. + * `list_generation_jobs` to enumerate recent jobs in the project. + * `delete_generation_job` to remove a finished job record. + * `delete_version` to remove the persisted evaluator that the job produced. + + `cancel_generation_job` is shown in a comment - cancelling requires catching + a job mid-flight (jobs usually finish in under two minutes), so it is not + exercised inline. + + Note: `delete_version` cascades to delete the generation job record as well, + so `delete_generation_job` may return 404 - that is expected and tolerated + below. + +USAGE: + python sample_rubric_evaluator_generation_lifecycle.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found + in the overview page of your Microsoft Foundry project. + 2) FOUNDRY_MODEL_NAME - Required. The name of the model the generation job + will use (e.g. `gpt-4o`, `gpt-4.1`). The generation runs inline server + side, so no deployment in your project is required. + 3) POLL_INTERVAL_SECONDS - Optional. Number of seconds to sleep between status + polls for the generation job. Defaults to 10. +""" + +import os +import time +import uuid +from datetime import datetime, timezone +from typing import cast + +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 JobStatus, PageOrder + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_name = os.environ["FOUNDRY_MODEL_NAME"] +poll_interval_seconds = int(os.environ.get("POLL_INTERVAL_SECONDS", "10")) + +# Unique per-run name so repeated runs do not collide. +ts = datetime.now(tz=timezone.utc).strftime("%Y%m%d%H%M%S") +short = uuid.uuid4().hex[:6] +evaluator_name = f"lifecycle-demo-{ts}-{short}" +operation_id = f"rubric-lifecycle-{short}" + +TERMINAL_STATUSES = {JobStatus.SUCCEEDED, JobStatus.FAILED, JobStatus.CANCELLED} + +# Shared job body used both for the initial create and the idempotency replay. +job_body = { + "model": model_name, + "name": "Lifecycle demo", + "evaluator_name": evaluator_name, + "evaluator_display_name": "Lifecycle demo", + "evaluator_description": "Minimal job used to demonstrate the LRO + list/delete lifecycle.", + "sources": [ + { + "type": "Prompt", + "description": "Inline application overview.", + "prompt": ( + "You are evaluating a simple Q&A assistant that answers factual " "questions clearly and concisely." + ), + } + ], +} + +with ( + DefaultAzureCredential() as credential, + # `allow_preview` and `api_version` are required for the evaluator + # generation endpoints in this preview. + AIProjectClient( + endpoint=endpoint, + credential=credential, + allow_preview=True, + api_version="2025-11-15-preview", + ) as project_client, +): + + # ------------------------------------------------------------------ + # 1. Create the generation job. + # ------------------------------------------------------------------ + # `operation_id` makes this call idempotent - re-running with the same id + # returns the existing job instead of creating a duplicate. Useful for + # retry-safe automation. + print(f"Create generation job with operation_id `{operation_id}`.") + job = project_client.beta.evaluators.create_generation_job(job=job_body, operation_id=operation_id) + print(f"Created generation job `{job.id}` (status: `{cast(JobStatus, job.status).value}`).") + + # Re-issuing the same operation_id returns the SAME job rather than + # starting a new one. + replay = project_client.beta.evaluators.create_generation_job(job=job_body, operation_id=operation_id) + assert replay.id == job.id, "operation_id should make create_generation_job idempotent" + print(f"Idempotent replay returned the same id `{replay.id}`.") + + # ------------------------------------------------------------------ + # 2. Poll the job to completion. + # ------------------------------------------------------------------ + print(f"Poll job `{job.id}` until it reaches a terminal state.", end="", flush=True) + while job.status not in TERMINAL_STATUSES: + time.sleep(poll_interval_seconds) + job = project_client.beta.evaluators.get_generation_job(job.id) + print(".", end="", flush=True) + print() + print(f"Final job status: `{cast(JobStatus, job.status).value}`.") + + if job.status != JobStatus.SUCCEEDED: + message = job.error.message if job.error is not None else "" + raise RuntimeError( + f"Generation job `{job.id}` ended with status `{cast(JobStatus, job.status).value}`: {message}" + ) + + evaluator = job.result + print(f"Generated evaluator: name=`{evaluator.name}` version=`{evaluator.version}`.") + + # ------------------------------------------------------------------ + # 3. List recent generation jobs in this project. + # ------------------------------------------------------------------ + # `PageOrder.DESC` returns the most recently created jobs first. + print("List the 5 most recent generation jobs in this project:") + recent = list(project_client.beta.evaluators.list_generation_jobs(limit=5, order=PageOrder.DESC)) + if not recent: + print(" (no jobs returned)") + for entry in recent: + print( + f" - id=`{entry.id}` status=`{cast(JobStatus, entry.status).value}` " + f"evaluator_name=`{entry.inputs.evaluator_name}`" + ) + + # ------------------------------------------------------------------ + # 4. Cancel (commented for reference). + # ------------------------------------------------------------------ + # To cancel a job, call `cancel_generation_job` while it is still running. + # The job above already completed, so the call is shown here only for + # reference. + # + # cancelled = project_client.beta.evaluators.cancel_generation_job(some_running_job_id) + # print(f"Cancelled: id=`{cancelled.id}` status=`{cast(JobStatus, cancelled.status).value}`.") + + # ------------------------------------------------------------------ + # 5. Clean up. + # ------------------------------------------------------------------ + print(f"Delete evaluator `{evaluator.name}` version `{evaluator.version}`.") + project_client.beta.evaluators.delete_version(name=evaluator.name, version=evaluator.version) + + # `delete_version` above cascades to remove the generation job record as + # well; tolerate a 404 here. + print(f"Delete generation job `{job.id}`.") + try: + project_client.beta.evaluators.delete_generation_job(job.id) + except ResourceNotFoundError: + print(f" Job `{job.id}` was already removed by the delete_version cascade.") diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_manual.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_manual.py new file mode 100644 index 000000000000..a6844a39b9fe --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_rubric_evaluator_manual.py @@ -0,0 +1,266 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + End-to-end scenario showing how to manually author a rubric-based + evaluator and use it as a testing criterion of an OpenAI evaluation run. + The sample: + + 1. Creates a rubric evaluator with `project_client.beta.evaluators.create_version`, + supplying scoring dimensions (each with an id, description, and integer + weight from 1-10) and an optional pass threshold. + 2. Creates an OpenAI evaluation (`client.evals.create`) referencing the + custom evaluator as a testing criterion. + 3. Runs the evaluation against inline JSONL sample data. + 4. Polls the evaluation run to completion and prints per-item results. + 5. Cleans up the evaluation and the evaluator version. + + A rubric evaluator is a collection of independent scoring dimensions. At + evaluation time, an LLM judge scores each applicable dimension on a 1-5 + scale and the runtime emits a normalized aggregate score. Dimensions can + opt in to `always_applicable` to skip the applicability assessment. + + See `sample_rubric_evaluator_generation_basic.py` for the generation-based + workflow that produces the same rubric structure automatically from a + description of the application. + +USAGE: + python sample_rubric_evaluator_manual.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found + in the overview page of your Microsoft Foundry project. + 2) FOUNDRY_MODEL_NAME - Required. The name of the LLM model deployment that + the rubric evaluator's judge will use at evaluation time (e.g. `gpt-4o`, `gpt-4.1`). + 3) POLL_INTERVAL_SECONDS - Optional. Number of seconds to sleep between status + polls for the evaluation run. Defaults to 10. +""" + +import os +import time +import uuid +from datetime import datetime, timezone + +from dotenv import load_dotenv +from openai.types.eval_create_params import DataSourceConfigCustom +from openai.types.evals.create_eval_jsonl_run_data_source_param import ( + CreateEvalJSONLRunDataSourceParam, + SourceFileContent, + SourceFileContentContent, +) + +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import ( + EvaluatorCategory, + EvaluatorDefinitionType, + TestingCriterionAzureAIEvaluator, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_name = os.environ["FOUNDRY_MODEL_NAME"] +poll_interval_seconds = int(os.environ.get("POLL_INTERVAL_SECONDS", "10")) + +# Unique per-run name so repeated runs do not collide. +ts = datetime.now(tz=timezone.utc).strftime("%Y%m%d%H%M%S") +short = uuid.uuid4().hex[:6] +evaluator_name = f"reservation-quality-manual-{ts}-{short}" + +TERMINAL_RUN_STATUSES = {"completed", "failed", "canceled"} + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + project_client.get_openai_client() as openai_client, +): + + # ------------------------------------------------------------------ + # 1. Author the rubric evaluator. + # ------------------------------------------------------------------ + # Each dimension is scored independently on a 1-5 scale at evaluation + # time. `weight` (1-10) controls how strongly each dimension contributes + # to the normalized aggregate score. + print(f"Create rubric evaluator `{evaluator_name}`.") + evaluator = project_client.beta.evaluators.create_version( + name=evaluator_name, + evaluator_version={ + "name": evaluator_name, + "categories": [EvaluatorCategory.QUALITY], + "display_name": "Reservation Quality (Manual)", + "description": ( + "Hand-authored rubric evaluating a reservation assistant on intent " + "resolution, completeness, and tone." + ), + "definition": { + "type": EvaluatorDefinitionType.RUBRIC, + "dimensions": [ + { + "id": "correct_intent_resolution", + "description": ( + "Correctly identifies the user's reservation intent (new booking, " + "modification, or cancellation) and pursues the right workflow." + ), + "weight": 9, + }, + { + "id": "completeness", + "description": ( + "Captures or confirms every reservation detail the user needs " + "(party size, date, time, contact info) before completing the task." + ), + "weight": 6, + }, + { + "id": "professional_tone", + "description": "Maintains a polite, professional, restaurant-host tone throughout.", + "weight": 3, + }, + ], + # `pass_threshold` sets the normalized 0.0-1.0 pass/fail threshold + # (default 0.5). The "any dimension scored 1 -> fail" rule applies + # regardless of this threshold. + "pass_threshold": 0.6, + }, + }, + ) + print(f"Created evaluator `{evaluator.name}` version `{evaluator.version}`.") + print(f"Categories: {[c.value for c in evaluator.categories]}") + print(f"Dimensions ({len(evaluator.definition.dimensions)}):") + for dim in evaluator.definition.dimensions: + marker = " [ALWAYS-ON]" if dim.always_applicable else "" + print(f" - {dim.id} (weight={dim.weight}){marker}") + + # ------------------------------------------------------------------ + # 2. Create an OpenAI evaluation that uses the rubric as a criterion. + # ------------------------------------------------------------------ + # The eval object describes the shape of the dataset items and the + # criteria to score against. The run below supplies inline sample data. + data_source_config = DataSourceConfigCustom( + type="custom", + item_schema={ + "type": "object", + "properties": { + "query": {"type": "string"}, + "response": {"type": "string"}, + }, + "required": ["query", "response"], + }, + include_sample_schema=True, + ) + + testing_criteria = [ + TestingCriterionAzureAIEvaluator( + type="azure_ai_evaluator", + name=evaluator_name, + evaluator_name=evaluator_name, + # The LLM judge for the rubric uses the deployment supplied here. + initialization_parameters={"deployment_name": model_name}, + data_mapping={ + "query": "{{item.query}}", + "response": "{{item.response}}", + }, + ) + ] + + print("Create the evaluation.") + eval_object = openai_client.evals.create( + name=f"{evaluator_name}-eval", + data_source_config=data_source_config, + testing_criteria=testing_criteria, + ) + print(f"Evaluation created (id: {eval_object.id}).") + + # ------------------------------------------------------------------ + # 3. Run the evaluation against inline JSONL sample data. + # ------------------------------------------------------------------ + print(f"Create an evaluation run for eval `{eval_object.id}`.") + eval_run = openai_client.evals.runs.create( + eval_id=eval_object.id, + name=f"{evaluator_name}-run", + metadata={"sample": "evaluator_rubric_manual"}, + data_source=CreateEvalJSONLRunDataSourceParam( + type="jsonl", + source=SourceFileContent( + type="file_content", + content=[ + SourceFileContentContent( + item={ + "query": "Can I book a table for 4 tomorrow at 7 PM?", + "response": ( + "Absolutely - I have you down for a table for 4 tomorrow at 7:00 PM. " + "Could you share a contact number in case anything changes?" + ), + } + ), + SourceFileContentContent( + item={ + "query": "I need to cancel my reservation for Friday.", + "response": "ok", + } + ), + SourceFileContentContent( + item={ + "query": "Can you move my Saturday 8 PM reservation to 8:30?", + "response": ( + "Of course. I've updated your Saturday reservation from 8:00 PM to 8:30 PM. " + "Anything else I can help with?" + ), + } + ), + ], + ), + ), + ) + print(f"Evaluation run created (id: {eval_run.id}).") + + print(f"Poll run `{eval_run.id}` until it reaches a terminal state.", end="", flush=True) + while eval_run.status not in TERMINAL_RUN_STATUSES: + time.sleep(poll_interval_seconds) + eval_run = openai_client.evals.runs.retrieve(run_id=eval_run.id, eval_id=eval_object.id) + print(".", end="", flush=True) + print() + print(f"Final eval run status: `{eval_run.status}`.") + + if eval_run.status == "completed": + print(f"Result counts: {eval_run.result_counts}") + if eval_run.report_url: + print(f"Eval run report URL: {eval_run.report_url}") + output_items = list(openai_client.evals.runs.output_items.list(run_id=eval_run.id, eval_id=eval_object.id)) + print(f"Output items (total: {len(output_items)}):") + for idx, item in enumerate(output_items, start=1): + results = getattr(item, "results", None) or [] + parts = [] + for r in results: + # Result entries are returned either as typed objects (Azure AI + # evaluators) or as plain dicts (some OpenAI-native evaluators). + if isinstance(r, dict): + name = r.get("name", "?") + score = r.get("score", "n/a") + passed = r.get("passed", "n/a") + else: + name = getattr(r, "name", "?") + score = getattr(r, "score", "n/a") + passed = getattr(r, "passed", "n/a") + parts.append(f"{name}={score} ({passed})") + print(f" item {idx}: status={item.status} | {', '.join(parts)}") + else: + print("Evaluation run did not complete successfully.") + + # ------------------------------------------------------------------ + # 4. Clean up. + # ------------------------------------------------------------------ + print(f"Delete evaluation `{eval_object.id}`.") + openai_client.evals.delete(eval_id=eval_object.id) + + print(f"Delete evaluator `{evaluator_name}` version `{evaluator.version}`.") + project_client.beta.evaluators.delete_version(name=evaluator_name, version=evaluator.version) diff --git a/sdk/ai/azure-ai-projects/samples/evaluations/sample_scheduled_evaluations.py b/sdk/ai/azure-ai-projects/samples/evaluations/sample_scheduled_evaluations.py index 85c89d83abad..5b66ce656b07 100644 --- a/sdk/ai/azure-ai-projects/samples/evaluations/sample_scheduled_evaluations.py +++ b/sdk/ai/azure-ai-projects/samples/evaluations/sample_scheduled_evaluations.py @@ -380,7 +380,7 @@ def schedule_redteam_evaluation() -> None: # pylint: disable=too-many-locals description="Taxonomy for red teaming evaluation", taxonomy_input=agent_taxonomy_input ) - taxonomy = project_client.beta.evaluation_taxonomies.create(name=agent_name, body=eval_taxonomy_input) + taxonomy = project_client.beta.evaluation_taxonomies.create(name=agent_name, taxonomy=eval_taxonomy_input) taxonomy_path = os.path.join(data_folder, f"taxonomy_{agent_name}.json") # Create the data folder if it doesn't exist os.makedirs(data_folder, exist_ok=True) diff --git a/sdk/ai/azure-ai-projects/samples/files/sample_files.py b/sdk/ai/azure-ai-projects/samples/files/sample_files.py index 8c96a934f300..6e6502a10193 100644 --- a/sdk/ai/azure-ai-projects/samples/files/sample_files.py +++ b/sdk/ai/azure-ai-projects/samples/files/sample_files.py @@ -39,7 +39,6 @@ AIProjectClient(endpoint=endpoint, credential=credential) as project_client, project_client.get_openai_client() as openai_client, ): - # [START files_sample] print("Uploading file") with open(file_path, "rb") as f: uploaded_file = openai_client.files.create(file=f, purpose="fine-tune") @@ -64,4 +63,3 @@ print(f"Deleting file with ID: {processed_file.id}") deleted_file = openai_client.files.delete(processed_file.id) print(f"Successfully deleted file: {deleted_file.id}") - # [END files_sample] diff --git a/sdk/ai/azure-ai-projects/samples/finetuning/sample_finetuning_oss_models_supervised_job.py b/sdk/ai/azure-ai-projects/samples/finetuning/sample_finetuning_oss_models_supervised_job.py index d944f157314c..15f5b6e26a2c 100644 --- a/sdk/ai/azure-ai-projects/samples/finetuning/sample_finetuning_oss_models_supervised_job.py +++ b/sdk/ai/azure-ai-projects/samples/finetuning/sample_finetuning_oss_models_supervised_job.py @@ -44,7 +44,6 @@ project_client.get_openai_client() as openai_client, ): - # [START finetuning_oss_model_supervised_job_sample] print("Uploading training file...") with open(training_file_path, "rb") as f: train_file = openai_client.files.create(file=f, purpose="fine-tune") @@ -75,4 +74,3 @@ # Learn more - https://azure.microsoft.com/pricing/details/cognitive-services/openai-service/ and https://azure.microsoft.com/explore/global-infrastructure/data-residency/ ) print(fine_tuning_job) - # [END finetuning_oss_model_supervised_job_sample] diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent-prebuilt.zip b/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent-prebuilt.zip new file mode 100644 index 000000000000..99c68e9b0b6b Binary files /dev/null and b/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent-prebuilt.zip differ diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent.zip b/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent.zip new file mode 100644 index 000000000000..133f1995d33a Binary files /dev/null and b/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent.zip differ diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/responses-echo-agent/Dockerfile b/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent/Dockerfile similarity index 100% rename from sdk/ai/azure-ai-projects/samples/hosted_agents/assets/responses-echo-agent/Dockerfile rename to sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent/Dockerfile diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/responses-echo-agent/README.md b/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent/README.md similarity index 100% rename from sdk/ai/azure-ai-projects/samples/hosted_agents/assets/responses-echo-agent/README.md rename to sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent/README.md diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/responses-echo-agent/main.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent/main.py similarity index 100% rename from sdk/ai/azure-ai-projects/samples/hosted_agents/assets/responses-echo-agent/main.py rename to sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent/main.py diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent/requirements.txt b/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent/requirements.txt new file mode 100644 index 000000000000..baffc50b482c --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/echo-agent/requirements.txt @@ -0,0 +1,3 @@ +azure-ai-agentserver-core==2.0.0b3 +azure-ai-agentserver-invocations==1.0.0b3 +azure-ai-agentserver-responses==1.0.0b5 diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/responses-echo-agent/requirements.txt b/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/responses-echo-agent/requirements.txt deleted file mode 100644 index 65ee9848e259..000000000000 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/assets/responses-echo-agent/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ ---index-url https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/ -azure-ai-agentserver-core==2.0.0a20260410006 -azure-ai-agentserver-invocations==1.0.0a20260410006 -azure-ai-agentserver-responses==1.0.0a20260410006 diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/hosted_agents_util.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/hosted_agents_util.py index 9641a6fb2616..ede784f3db33 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/hosted_agents_util.py +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/hosted_agents_util.py @@ -1,12 +1,44 @@ import asyncio -import logging +import hashlib import time -from contextlib import asynccontextmanager, contextmanager -from typing import AsyncGenerator, Optional +from pathlib import Path +from typing import Tuple from azure.ai.projects import AIProjectClient from azure.ai.projects.aio import AIProjectClient as AsyncAIProjectClient -from azure.ai.projects.models import HostedAgentDefinition, ProtocolVersionRecord, VersionRefIndicator +from azure.ai.projects.models import ( + AgentVersionDetails, + CodeDependencyResolution, +) + +_ASSETS_DIR = Path(__file__).parent / "assets" + + +def select_echo_agent_code_zip( + use_remote_build: bool, +) -> Tuple[CodeDependencyResolution, str, bytes, str]: + """Pick the dependency-resolution mode and matching echo-agent zip, and load it. + + When ``use_remote_build`` is ``True``, returns REMOTE_BUILD with + ``assets/echo-agent.zip``; otherwise BUNDLED with + ``assets/echo-agent-prebuilt.zip``. + + Reads the zip bytes, computes its SHA-256, and prints a one-line summary. + + Returns ``(dependency_resolution, zip_filename, zip_bytes, zip_sha256)``. + """ + dependency_resolution = ( + CodeDependencyResolution.REMOTE_BUILD if use_remote_build else CodeDependencyResolution.BUNDLED + ) + zip_filename = "echo-agent.zip" if use_remote_build else "echo-agent-prebuilt.zip" + zip_path = _ASSETS_DIR / zip_filename + zip_bytes = zip_path.read_bytes() + zip_sha256 = hashlib.sha256(zip_bytes).hexdigest() + print( + f"Loaded code zip from {zip_path} (dependency_resolution={dependency_resolution.value}): " + f"{len(zip_bytes)} bytes, sha256={zip_sha256}" + ) + return dependency_resolution, zip_filename, zip_bytes, zip_sha256 def wait_for_agent_version_active( @@ -14,34 +46,26 @@ def wait_for_agent_version_active( agent_name: str, agent_version: str, *, - logger: Optional[logging.Logger] = None, max_attempts: int = 60, poll_interval_seconds: int = 10, ) -> None: - if logger: - logger.info("Waiting for agent version to become active...") + """Poll until the version becomes ``active``; raise on ``failed`` or timeout.""" + print("Waiting for agent version to become active...") for attempt in range(max_attempts): time.sleep(poll_interval_seconds) version_details = project_client.agents.get_version(agent_name=agent_name, agent_version=agent_version) - status = version_details.status + status = version_details["status"] - if logger: - logger.debug(f"Agent version status: {status} (attempt {attempt + 1}/{max_attempts})") - print(f"Agent version status: {status} (attempt {attempt + 1})") + print(f"Agent version status: {status} (attempt {attempt + 1}/{max_attempts})") if status == "active": - if logger: - logger.info("Agent version is now active") + print("Agent version is now active") return if status == "failed": - if logger: - logger.error(f"Agent version provisioning failed: {dict(version_details)}") raise RuntimeError(f"Agent version provisioning failed: {dict(version_details)}") - if logger: - logger.error("Timed out waiting for agent version to become active") raise RuntimeError("Timed out waiting for agent version to become active") @@ -50,128 +74,52 @@ async def wait_for_agent_version_active_async( agent_name: str, agent_version: str, *, - logger: Optional[logging.Logger] = None, max_attempts: int = 60, poll_interval_seconds: int = 10, ) -> None: - if logger: - logger.info("Waiting for agent version to become active...") + """Async variant of :func:`wait_for_agent_version_active`.""" + print("Waiting for agent version to become active...") for attempt in range(max_attempts): await asyncio.sleep(poll_interval_seconds) version_details = await project_client.agents.get_version(agent_name=agent_name, agent_version=agent_version) status = version_details["status"] - if logger: - logger.debug(f"Agent version status: {status} (attempt {attempt + 1}/{max_attempts})") - print(f"Agent version status: {status} (attempt {attempt + 1})") + print(f"Agent version status: {status} (attempt {attempt + 1}/{max_attempts})") if status == "active": - if logger: - logger.info("Agent version is now active") + print("Agent version is now active") return if status == "failed": - if logger: - logger.error(f"Agent version provisioning failed: {dict(version_details)}") raise RuntimeError(f"Agent version provisioning failed: {dict(version_details)}") - if logger: - logger.error("Timed out waiting for agent version to become active") raise RuntimeError("Timed out waiting for agent version to become active") -@contextmanager -def create_agent_and_session( +def get_latest_active_agent_version( project_client: AIProjectClient, agent_name: str, - image: str, - isolation_key: str = "sample-isolation-key", -): - agent = project_client.agents.create_version( - agent_name=agent_name, - definition=HostedAgentDefinition( - cpu="0.5", - memory="1Gi", - image=image, - container_protocol_versions=[ - ProtocolVersionRecord(protocol="responses", version="1.0.0"), - ], - ), - metadata={"enableVnextExperience": "true"}, +) -> AgentVersionDetails: + for version in project_client.agents.list_versions(agent_name=agent_name, order="desc"): + if version.status == "active": + return version + + raise RuntimeError( + f"No active version found for hosted agent '{agent_name}'. " + "Create or activate a version before running this sample." ) - print(f"Agent created (name: {agent.name}, version: {agent.version})") - wait_for_agent_version_active( - project_client=project_client, - agent_name=agent_name, - agent_version=agent.version, - ) - session = project_client.beta.agents.create_session( - agent_name=agent_name, - isolation_key=isolation_key, - version_indicator=VersionRefIndicator(agent_version=agent.version), - ) - print(f"Session created (id: {session.agent_session_id}, status: {session.status})") - - try: - yield agent, session - finally: - project_client.beta.agents.delete_session( - agent_name=agent_name, - session_id=session.agent_session_id, - isolation_key=isolation_key, - ) - print(f"Session with id: {session.agent_session_id} deleted.") - - project_client.agents.delete_version(agent_name=agent_name, agent_version=agent.version) - print(f"Agent version {agent.version} deleted.") - - -@asynccontextmanager -async def create_agent_and_session_async( +async def get_latest_active_agent_version_async( project_client: AsyncAIProjectClient, agent_name: str, - image: str, - isolation_key: str = "sample-isolation-key", -) -> AsyncGenerator[tuple[str, str], None]: - agent = await project_client.agents.create_version( - agent_name=agent_name, - definition=HostedAgentDefinition( - cpu="0.5", - memory="1Gi", - image=image, - container_protocol_versions=[ - ProtocolVersionRecord(protocol="responses", version="1.0.0"), - ], - ), - metadata={"enableVnextExperience": "true"}, - ) - print(f"Agent created (name: {agent.name}, version: {agent.version})") - - await wait_for_agent_version_active_async( - project_client=project_client, - agent_name=agent_name, - agent_version=agent.version, - ) - - session = await project_client.beta.agents.create_session( - agent_name=agent_name, - isolation_key=isolation_key, - version_indicator=VersionRefIndicator(agent_version=agent.version), +) -> AgentVersionDetails: + async for version in project_client.agents.list_versions(agent_name=agent_name, order="desc"): + if version.status == "active": + return version + + raise RuntimeError( + f"No active version found for hosted agent '{agent_name}'. " + "Create or activate a version before running this sample." ) - print(f"Session created (id: {session.agent_session_id}, status: {session.status})") - - try: - yield agent.version, session.agent_session_id - finally: - await project_client.beta.agents.delete_session( - agent_name=agent_name, - session_id=session.agent_session_id, - isolation_key=isolation_key, - ) - print(f"Session with id: {session.agent_session_id} deleted.") - - await project_client.agents.delete_version(agent_name=agent_name, agent_version=agent.version) - print(f"Agent version {agent.version} deleted.") diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/rbac_util.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/rbac_util.py new file mode 100644 index 000000000000..40ef3c3cf6a4 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/rbac_util.py @@ -0,0 +1,252 @@ +import uuid +from typing import Any, cast +from urllib.parse import urlparse + +from azure.core.credentials import TokenCredential +from azure.core.credentials_async import AsyncTokenCredential +from azure.core.exceptions import ResourceNotFoundError +from azure.mgmt.authorization import AuthorizationManagementClient, models as authorization_models +from azure.mgmt.authorization.aio import AuthorizationManagementClient as AsyncAuthorizationManagementClient +from azure.mgmt.resource import ResourceManagementClient +from azure.mgmt.resource.resources.aio import ResourceManagementClient as AsyncResourceManagementClient +from azure.ai.projects.models import AgentVersionDetails + +AZURE_AI_USER_ROLE_DEFINITION_GUID = "53ca6127-db72-4b80-b1b0-d745d6d5456d" + + +def _extract_resource_group_name(resource_id: str) -> str: + parts = resource_id.strip("/").split("/") + for index, part in enumerate(parts): + if part.lower() == "resourcegroups" and index + 1 < len(parts): + return parts[index + 1] + return "" + + +def _resolve_ai_account_resource_id( + credential: TokenCredential, + account_name: str, + project_name: str, + subscription_id: str, +) -> str: + resource_client = ResourceManagementClient(credential, subscription_id) + project_resources = resource_client.resources.list( + filter="resourceType eq 'Microsoft.CognitiveServices/accounts/projects'" + ) + + project_id_segment = f"/accounts/{account_name}/projects/{project_name}".lower() + matching_projects = [ + resource for resource in project_resources if resource.id and project_id_segment in resource.id.lower() + ] + if not matching_projects: + raise RuntimeError(f"Could not locate Foundry project '{project_name}' in subscription '{subscription_id}'.") + + if not matching_projects[0].id: + raise RuntimeError("Foundry project resource ID is empty.") + resource_group_name = _extract_resource_group_name(matching_projects[0].id) + account_resources = resource_client.resources.list_by_resource_group( + resource_group_name=resource_group_name, + filter="resourceType eq 'Microsoft.CognitiveServices/accounts'", + ) + + account_matches = [resource.id for resource in account_resources if resource.name == account_name and resource.id] + if not account_matches: + raise RuntimeError( + f"Could not locate Azure AI account '{account_name}' in resource group '{resource_group_name}'." + ) + return account_matches[0] + + +def _ensure_agent_identity_rbac_with_role_id( + credential: TokenCredential, principal_id: str, scope_resource_id: str, subscription_id: str, role_id: str +) -> tuple[bool, str]: + authorization_client = AuthorizationManagementClient(credential, subscription_id) + role_definition_id = f"/subscriptions/{subscription_id}/providers/Microsoft.Authorization/roleDefinitions/{role_id}" + role_assignment_name = str( + uuid.uuid5( + uuid.NAMESPACE_URL, + f"{scope_resource_id}|{principal_id}|{role_definition_id}", + ) + ) + + try: + authorization_client.role_assignments.get(scope_resource_id, role_assignment_name) + print(f"Azure AI User role already assigned to principal {principal_id}.") + return False, role_assignment_name + except ResourceNotFoundError: + pass + + create_parameters_kwargs = cast( + dict[str, Any], + { + "role_definition_id": role_definition_id, + "principal_id": principal_id, + "principal_type": authorization_models.PrincipalType.SERVICE_PRINCIPAL, + }, + ) + parameters = authorization_models.RoleAssignmentCreateParameters(**create_parameters_kwargs) + + authorization_client.role_assignments.create(scope_resource_id, role_assignment_name, parameters) + print(f"Assigned Azure AI User role to principal {principal_id} at scope {scope_resource_id}.") + return True, role_assignment_name + + +def ensure_agent_identity_rbac( + agent: AgentVersionDetails, + credential: TokenCredential, + subscription_id: str, + foundry_project_endpoint: str, +) -> None: + """Ensure the hosted agent identity has Azure AI User role on the Azure AI account. + + This resolves the Azure AI account resource ID from the Foundry project endpoint, + reads the hosted agent managed identity principal ID from ``agent``, and + creates a deterministic role assignment for the Azure AI User role if one does not + already exist. + + :param agent: Agent version details containing ``instance_identity``. + :type agent: ~azure.ai.projects.models.AgentVersionDetails + :param credential: Credential used for Azure Resource Manager authorization calls. + :type credential: ~azure.core.credentials.TokenCredential + :param subscription_id: Azure subscription ID containing the Foundry project/account. + :type subscription_id: str + :param foundry_project_endpoint: Foundry project endpoint in the format + ``https://.services.ai.azure.com/api/projects/``. + :type foundry_project_endpoint: str + :raises RuntimeError: If the agent identity principal ID is unavailable, or if the + account/project resources cannot be resolved. + :raises ~azure.core.exceptions.HttpResponseError: If role assignment creation fails + for reasons other than an existing assignment. + """ + if not agent.instance_identity or not agent.instance_identity.principal_id: + raise RuntimeError("Agent instance_identity or principal_id is not available.") + principal_id = agent.instance_identity.principal_id + + account_name = urlparse(foundry_project_endpoint).hostname.split(".")[0] # type: ignore[union-attr] + project_name = foundry_project_endpoint.rstrip("/").split("/api/projects/")[1].split("/")[0] + scope_resource_id = _resolve_ai_account_resource_id(credential, account_name, project_name, subscription_id) + + _ensure_agent_identity_rbac_with_role_id( + credential=credential, + principal_id=principal_id, + scope_resource_id=scope_resource_id, + subscription_id=subscription_id, + role_id=AZURE_AI_USER_ROLE_DEFINITION_GUID, + ) + + +async def _resolve_ai_account_resource_id_async( + credential: AsyncTokenCredential, + account_name: str, + project_name: str, + subscription_id: str, +) -> str: + async with AsyncResourceManagementClient(credential, subscription_id) as resource_client: + project_id_segment = f"/accounts/{account_name}/projects/{project_name}".lower() + matching_projects = [] + async for resource in resource_client.resources.list( + filter="resourceType eq 'Microsoft.CognitiveServices/accounts/projects'" + ): + if resource.id and project_id_segment in resource.id.lower(): + matching_projects.append(resource) + if not matching_projects: + raise RuntimeError( + f"Could not locate Foundry project '{project_name}' in subscription '{subscription_id}'." + ) + + if not matching_projects[0].id: + raise RuntimeError("Foundry project resource ID is empty.") + resource_group_name = _extract_resource_group_name(matching_projects[0].id) + + account_matches = [] + async for resource in resource_client.resources.list_by_resource_group( + resource_group_name=resource_group_name, + filter="resourceType eq 'Microsoft.CognitiveServices/accounts'", + ): + if resource.name == account_name and resource.id: + account_matches.append(resource.id) + if not account_matches: + raise RuntimeError( + f"Could not locate Azure AI account '{account_name}' in resource group '{resource_group_name}'." + ) + return account_matches[0] + + +async def _ensure_agent_identity_rbac_with_role_id_async( + credential: AsyncTokenCredential, + principal_id: str, + scope_resource_id: str, + subscription_id: str, + role_id: str, +) -> tuple[bool, str]: + async with AsyncAuthorizationManagementClient(credential, subscription_id) as authorization_client: + role_definition_id = ( + f"/subscriptions/{subscription_id}/providers/Microsoft.Authorization/roleDefinitions/{role_id}" + ) + role_assignment_name = str( + uuid.uuid5( + uuid.NAMESPACE_URL, + f"{scope_resource_id}|{principal_id}|{role_definition_id}", + ) + ) + + try: + await authorization_client.role_assignments.get(scope_resource_id, role_assignment_name) + print(f"Azure AI User role already assigned to principal {principal_id}.") + return False, role_assignment_name + except ResourceNotFoundError: + pass + + create_parameters_kwargs = cast( + dict[str, Any], + { + "role_definition_id": role_definition_id, + "principal_id": principal_id, + "principal_type": authorization_models.PrincipalType.SERVICE_PRINCIPAL, + }, + ) + parameters = authorization_models.RoleAssignmentCreateParameters(**create_parameters_kwargs) + + await authorization_client.role_assignments.create(scope_resource_id, role_assignment_name, parameters) + print(f"Assigned Azure AI User role to principal {principal_id} at scope {scope_resource_id}.") + return True, role_assignment_name + + +async def ensure_agent_identity_rbac_async( + agent: AgentVersionDetails, + credential: AsyncTokenCredential, + subscription_id: str, + foundry_project_endpoint: str, +) -> None: + """Async variant of :func:`ensure_agent_identity_rbac`. + + :param agent: Agent version details containing ``instance_identity``. + :type agent: ~azure.ai.projects.models.AgentVersionDetails + :param credential: Async credential used for Azure Resource Manager authorization calls. + :type credential: ~azure.core.credentials_async.AsyncTokenCredential + :param subscription_id: Azure subscription ID containing the Foundry project/account. + :type subscription_id: str + :param foundry_project_endpoint: Foundry project endpoint in the format + ``https://.services.ai.azure.com/api/projects/``. + :type foundry_project_endpoint: str + :raises RuntimeError: If the agent identity principal ID is unavailable, or if the + account/project resources cannot be resolved. + :raises ~azure.core.exceptions.HttpResponseError: If role assignment creation fails + for reasons other than an existing assignment. + """ + if not agent.instance_identity or not agent.instance_identity.principal_id: + raise RuntimeError("Agent instance_identity or principal_id is not available.") + principal_id = agent.instance_identity.principal_id + + account_name = urlparse(foundry_project_endpoint).hostname.split(".")[0] # type: ignore[union-attr] + project_name = foundry_project_endpoint.rstrip("/").split("/api/projects/")[1].split("/")[0] + scope_resource_id = await _resolve_ai_account_resource_id_async( + credential, account_name, project_name, subscription_id + ) + + await _ensure_agent_identity_rbac_with_role_id_async( + credential=credential, + principal_id=principal_id, + scope_resource_id=scope_resource_id, + subscription_id=subscription_id, + role_id=AZURE_AI_USER_ROLE_DEFINITION_GUID, + ) diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_agent_endpoint.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_agent_endpoint.py index 0833548e857e..82be3e7df1ce 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_agent_endpoint.py +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_agent_endpoint.py @@ -6,7 +6,7 @@ """ DESCRIPTION: - This sample demonstrates how to create a Hosted Agent and Session, + This sample demonstrates how to use an existing Hosted Agent and create a Session, configure an Agent endpoint for Responses protocol, and invoke the OpenAI Responses API against that agent endpoint using the synchronous AIProjectClient. @@ -27,11 +27,10 @@ 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_AGENT_CONTAINER_IMAGE - The Hosted Agent container image in the format '/[:|@]' + 2) FOUNDRY_HOSTED_AGENT_NAME - The name of an existing Hosted Agent. - You can build and push an example image from - `samples/hosted_agents/assets/responses-echo-agent` and use that image value - for `FOUNDRY_AGENT_CONTAINER_IMAGE`. + If you don't have a Hosted Agent, run `sample_create_hosted_agent.py` or + `sample_create_hosted_agent_from_code.py` first to create one as a prerequisite. """ import os @@ -42,18 +41,18 @@ from azure.ai.projects import AIProjectClient from azure.ai.projects.models import ( - AgentEndpoint, + AgentEndpointConfig, AgentEndpointProtocol, FixedRatioVersionSelectionRule, VersionSelector, ) -from hosted_agents_util import create_agent_and_session +from azure.ai.projects.models import VersionRefIndicator +from hosted_agents_util import get_latest_active_agent_version load_dotenv() endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] -image = os.environ["FOUNDRY_AGENT_CONTAINER_IMAGE"] -agent_name = "MySessionHostedAgent" +agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] with ( DefaultAzureCredential() as credential, @@ -62,33 +61,47 @@ credential=credential, allow_preview=True, ) as project_client, - create_agent_and_session(project_client, agent_name, image) as (agent, session), ): - # Configure endpoint routing so this agent name serves the created version. - # 100% of traffic is routed to the single created version. - endpoint_config = AgentEndpoint( - version_selector=VersionSelector( - version_selection_rules=[ - FixedRatioVersionSelectionRule(agent_version=agent.version, traffic_percentage=100), - ] - ), - protocols=[AgentEndpointProtocol.RESPONSES], - ) - patched_agent = project_client.beta.agents.patch_agent_details( - agent_name=agent_name, - agent_endpoint=endpoint_config, - ) - print(f"Agent endpoint configured for agent: {patched_agent.name}") + agent = get_latest_active_agent_version(project_client, agent_name) - # Create an OpenAI client bound to the agent endpoint. - openai_client = project_client.get_openai_client(agent_name=agent_name) - - # Call Responses API and bind the request to the created agent session. - response = openai_client.responses.create( - input="What is the size of France in square miles?", - extra_body={ - "agent_session_id": session.agent_session_id, - }, + session = project_client.beta.agents.create_session( + agent_name=agent_name, + version_indicator=VersionRefIndicator(agent_version=agent.version), ) - print(f"Response output: {response.output_text}") + print(f"Session created (id: {session.agent_session_id}, status: {session.status})") + try: + # Configure endpoint routing so this agent name serves the created version. + # 100% of traffic is routed to the single created version. + endpoint_config = AgentEndpointConfig( + version_selector=VersionSelector( + version_selection_rules=[ + FixedRatioVersionSelectionRule(agent_version=agent.version, traffic_percentage=100), + ] + ), + protocols=[AgentEndpointProtocol.RESPONSES], + ) + + patched_agent = project_client.beta.agents.patch_agent_details( + agent_name=agent_name, + agent_endpoint=endpoint_config, + ) + print(f"Agent endpoint configured for agent: {patched_agent.name}") + + # Create an OpenAI client bound to the agent endpoint. + openai_client = project_client.get_openai_client(agent_name=agent_name) + + # Call Responses API and bind the request to the created agent session. + response = openai_client.responses.create( + input="What is the size of France in square miles?", + extra_body={ + "agent_session_id": session.agent_session_id, + }, + ) + print(f"Response output: {response.output_text}") + finally: + project_client.beta.agents.delete_session( + agent_name=agent_name, + session_id=session.agent_session_id, + ) + print(f"Session deleted (id: {session.agent_session_id})") diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_agent_endpoint_async.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_agent_endpoint_async.py index 91a9d8ac2437..94649b7ad73a 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_agent_endpoint_async.py +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_agent_endpoint_async.py @@ -6,7 +6,7 @@ """ DESCRIPTION: - This sample demonstrates how to create a Hosted Agent and Session, + This sample demonstrates how to use an existing Hosted Agent and create a Session, configure an Agent endpoint for Responses protocol, and invoke the OpenAI Responses API against that agent endpoint using the asynchronous AIProjectClient. @@ -27,11 +27,10 @@ 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_AGENT_CONTAINER_IMAGE - The Hosted Agent container image in the format '/[:|@]' + 2) FOUNDRY_HOSTED_AGENT_NAME - The name of an existing Hosted Agent. - You can build and push an example image from - `samples/hosted_agents/assets/responses-echo-agent` and use that image value - for `FOUNDRY_AGENT_CONTAINER_IMAGE`. + If you don't have a Hosted Agent, run `sample_create_hosted_agent_async.py` or + `sample_create_hosted_agent_from_code_async.py` first to create one as a prerequisite. """ import asyncio @@ -43,21 +42,21 @@ from azure.ai.projects.aio import AIProjectClient from azure.ai.projects.models import ( - AgentEndpoint, + AgentEndpointConfig, AgentEndpointProtocol, FixedRatioVersionSelectionRule, VersionSelector, ) -from hosted_agents_util import create_agent_and_session_async +from azure.ai.projects.models import VersionRefIndicator +from hosted_agents_util import get_latest_active_agent_version_async load_dotenv() -endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] -image = os.environ["FOUNDRY_AGENT_CONTAINER_IMAGE"] -agent_name = "MySessionHostedAgent" +async def main(): + endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] -async def main() -> None: async with ( DefaultAzureCredential() as credential, AIProjectClient( @@ -65,35 +64,50 @@ async def main() -> None: credential=credential, allow_preview=True, ) as project_client, - create_agent_and_session_async(project_client, agent_name, image) as (agent_version, session_id), ): - # Configure endpoint routing so this agent name serves the created version. - # 100% of traffic is routed to the single created version. - endpoint_config = AgentEndpoint( - version_selector=VersionSelector( - version_selection_rules=[ - FixedRatioVersionSelectionRule(agent_version=agent_version, traffic_percentage=100), - ] - ), - protocols=[AgentEndpointProtocol.RESPONSES], - ) - patched_agent = await project_client.beta.agents.patch_agent_details( + agent = await get_latest_active_agent_version_async(project_client, agent_name) + + session = await project_client.beta.agents.create_session( agent_name=agent_name, - agent_endpoint=endpoint_config, + version_indicator=VersionRefIndicator(agent_version=agent.version), ) - print(f"Agent endpoint configured for agent: {patched_agent.name}") + print(f"Session created (id: {session.agent_session_id}, status: {session.status})") + try: + # Configure endpoint routing so this agent name serves the created version. + # 100% of traffic is routed to the single created version. + endpoint_config = AgentEndpointConfig( + version_selector=VersionSelector( + version_selection_rules=[ + FixedRatioVersionSelectionRule(agent_version=agent.version, traffic_percentage=100), + ] + ), + protocols=[AgentEndpointProtocol.RESPONSES], + ) + + patched_agent = await project_client.beta.agents.patch_agent_details( + agent_name=agent_name, + agent_endpoint=endpoint_config, + ) + print(f"Agent endpoint configured for agent: {patched_agent.name}") + + # Create an OpenAI client bound to the agent endpoint. + openai_client = project_client.get_openai_client(agent_name=agent_name) - # Create an OpenAI client bound to the agent endpoint. - async with project_client.get_openai_client(agent_name=agent_name) as openai_client: # Call Responses API and bind the request to the created agent session. response = await openai_client.responses.create( input="What is the size of France in square miles?", extra_body={ - "agent_session_id": session_id, + "agent_session_id": session.agent_session_id, }, ) print(f"Response output: {response.output_text}") + finally: + await project_client.beta.agents.delete_session( + agent_name=agent_name, + session_id=session.agent_session_id, + ) + print(f"Session deleted (id: {session.agent_session_id})") if __name__ == "__main__": diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent.py new file mode 100644 index 000000000000..e3e9530626a1 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent.py @@ -0,0 +1,88 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + This sample demonstrates CRUD operations for Hosted Agent versions + using the synchronous AIProjectClient. + + This is the only hosted_agents sample that sets up agent identity RBAC + via `ensure_agent_identity_rbac`. + +USAGE: + python sample_create_hosted_agent.py + + Before running the sample: + + pip install "azure-ai-projects>=2.1.0" azure-mgmt-authorization azure-mgmt-resource 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 Hosted Agent name. + 3) FOUNDRY_AGENT_CONTAINER_IMAGE - The Hosted Agent container image in the format + '/[:|@]'. + You can build a sample image from the `samples/hosted_agents/assets/echo-agent` folder. + 4) AZURE_SUBSCRIPTION_ID - Azure subscription ID where the + Azure AI account and project are deployed. +""" + +import os + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential + +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import HostedAgentDefinition, ProtocolVersionRecord +from hosted_agents_util import wait_for_agent_version_active +from rbac_util import ensure_agent_identity_rbac + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] +image = os.environ["FOUNDRY_AGENT_CONTAINER_IMAGE"] +subscription_id = os.environ["AZURE_SUBSCRIPTION_ID"] + + +with ( + DefaultAzureCredential() as credential, + AIProjectClient( + endpoint=endpoint, + credential=credential, + allow_preview=True, + ) as project_client, +): + created = project_client.agents.create_version( + agent_name=agent_name, + definition=HostedAgentDefinition( + cpu="0.5", + memory="1Gi", + image=image, + container_protocol_versions=[ + ProtocolVersionRecord(protocol="responses", version="1.0.0"), + ], + ), + metadata={"enableVnextExperience": "true"}, + ) + print(f"Created hosted agent version: {created.version}") + + wait_for_agent_version_active( + project_client=project_client, + agent_name=agent_name, + agent_version=created.version, + ) + + ensure_agent_identity_rbac( + agent=created, + credential=credential, + subscription_id=subscription_id, + foundry_project_endpoint=endpoint, + ) + + fetched = project_client.agents.get_version(agent_name=agent_name, agent_version=created.version) + print(f"Fetched hosted agent version: {fetched.version}, status: {fetched.status}") diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent_async.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent_async.py new file mode 100644 index 000000000000..fcdf109a15ed --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent_async.py @@ -0,0 +1,94 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Async variant of `sample_create_hosted_agent.py`. Demonstrates CRUD + operations for Hosted Agent versions using the asynchronous AIProjectClient. + + This is the only hosted_agents async sample that sets up agent identity + RBAC via `ensure_agent_identity_rbac_async`. + +USAGE: + python sample_create_hosted_agent_async.py + + Before running the sample: + + pip install "azure-ai-projects>=2.1.0" aiohttp azure-mgmt-authorization azure-mgmt-resource 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 Hosted Agent name. + 3) FOUNDRY_AGENT_CONTAINER_IMAGE - The Hosted Agent container image in the format + '/[:|@]'. + You can build a sample image from the `samples/hosted_agents/assets/echo-agent` folder. + 4) AZURE_SUBSCRIPTION_ID - Azure subscription ID where the + Azure AI account and project are deployed. +""" + +import asyncio +import os + +from dotenv import load_dotenv + +from azure.identity.aio import DefaultAzureCredential + +from azure.ai.projects.aio import AIProjectClient +from azure.ai.projects.models import HostedAgentDefinition, ProtocolVersionRecord +from hosted_agents_util import wait_for_agent_version_active_async +from rbac_util import ensure_agent_identity_rbac_async + + +async def main() -> None: + load_dotenv() + + endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] + image = os.environ["FOUNDRY_AGENT_CONTAINER_IMAGE"] + subscription_id = os.environ["AZURE_SUBSCRIPTION_ID"] + + async with ( + DefaultAzureCredential() as credential, + AIProjectClient( + endpoint=endpoint, + credential=credential, + allow_preview=True, + ) as project_client, + ): + created = await project_client.agents.create_version( + agent_name=agent_name, + definition=HostedAgentDefinition( + cpu="0.5", + memory="1Gi", + image=image, + container_protocol_versions=[ + ProtocolVersionRecord(protocol="responses", version="1.0.0"), + ], + ), + metadata={"enableVnextExperience": "true"}, + ) + print(f"Created hosted agent version: {created.version}") + + await wait_for_agent_version_active_async( + project_client=project_client, + agent_name=agent_name, + agent_version=created.version, + ) + + await ensure_agent_identity_rbac_async( + agent=created, + credential=credential, + subscription_id=subscription_id, + foundry_project_endpoint=endpoint, + ) + + fetched = await project_client.agents.get_version(agent_name=agent_name, agent_version=created.version) + print(f"Fetched hosted agent version: {fetched.version}, status: {fetched.status}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent_from_code.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent_from_code.py new file mode 100644 index 000000000000..cdd4a3396125 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent_from_code.py @@ -0,0 +1,127 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Upload a code zip as a new version of a code-based Hosted Agent, + poll for provisioning, and download it back to verify the round-trip. + + The dependency resolution mode is selected via the + `FOUNDRY_HOSTED_AGENT_REMOTE_BUILD` environment variable (default: `false`): + + * `false` (BUNDLED) — uploads `assets/echo-agent-prebuilt.zip`, which + bundles the agent source plus a `packages/` folder with Linux-built + dependencies, so the service skips pip entirely. + * `true` (REMOTE_BUILD) — uploads `assets/echo-agent.zip`, which contains + only the agent source plus `requirements.txt`; the service resolves + dependencies remotely from the public package index. + + The agent must already exist; create it with + `samples/hosted_agents/sample_create_hosted_agent.py`. + +USAGE: + python sample_create_hosted_agent_from_code.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 Hosted Agent name. Must already exist. + 3) AZURE_SUBSCRIPTION_ID - Azure subscription ID where the Azure AI account + and project are deployed. + 4) FOUNDRY_HOSTED_AGENT_REMOTE_BUILD - Optional. Set to `true` to use + REMOTE_BUILD; defaults to `false` (BUNDLED). +""" + +import hashlib +import os +import tempfile +from pathlib import Path + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential + +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import ( + CodeConfiguration, + CreateAgentVersionFromCodeContent, + CreateAgentVersionFromCodeMetadata, + HostedAgentDefinition, + ProtocolVersionRecord, +) + +from hosted_agents_util import select_echo_agent_code_zip, wait_for_agent_version_active +from rbac_util import ensure_agent_identity_rbac + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] +subscription_id = os.environ["AZURE_SUBSCRIPTION_ID"] +use_remote_build = os.environ.get("FOUNDRY_HOSTED_AGENT_REMOTE_BUILD", "false").strip().lower() == "true" + +dependency_resolution, zip_filename, code_zip_bytes, code_zip_sha256 = select_echo_agent_code_zip(use_remote_build) + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client, +): + content = CreateAgentVersionFromCodeContent( + metadata=CreateAgentVersionFromCodeMetadata( + description=f"Code-based hosted agent uploaded with dependency_resolution={dependency_resolution.value}.", + definition=HostedAgentDefinition( + cpu="0.5", + memory="1Gi", + code_configuration=CodeConfiguration( + runtime="python_3_12", + entry_point=["python", "main.py"], + dependency_resolution=dependency_resolution, + ), + protocol_versions=[ProtocolVersionRecord(protocol="responses", version="1.0.0")], + ), + ), + code=(zip_filename, code_zip_bytes, "application/zip"), + ) + + created = project_client.beta.agents.create_version_from_code( + agent_name=agent_name, + content=content, + code_zip_sha256=code_zip_sha256, + ) + print(f"Created code-based hosted agent version: {created.version}") + + wait_for_agent_version_active( + project_client=project_client, + agent_name=agent_name, + agent_version=created.version, + ) + + ensure_agent_identity_rbac( + agent=created, + credential=credential, + subscription_id=subscription_id, + foundry_project_endpoint=endpoint, + ) + + # Download the zip for the version we just created, streaming to a temp file. + version_zip_path = Path(tempfile.gettempdir()) / f"{agent_name}-{created.version}.zip" + sha = hashlib.sha256() + with open(version_zip_path, "wb") as f: + for chunk in project_client.beta.agents.download_code( + agent_name=agent_name, + agent_version=created.version, + ): + f.write(chunk) + sha.update(chunk) + downloaded_version_sha256 = sha.hexdigest() + print( + f"Downloaded version code zip to {version_zip_path}: {version_zip_path.stat().st_size} bytes, " + f"sha256={downloaded_version_sha256} (matches uploaded: {downloaded_version_sha256 == code_zip_sha256})" + ) diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent_from_code_async.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent_from_code_async.py new file mode 100644 index 000000000000..cd05ad4b472e --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_create_hosted_agent_from_code_async.py @@ -0,0 +1,138 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Async variant of `sample_create_hosted_agent_from_code.py`. Uploads a code + zip as a new version of a code-based Hosted Agent, polls for provisioning, + and downloads it back to verify the round-trip. + + The dependency resolution mode is selected via the + `FOUNDRY_HOSTED_AGENT_REMOTE_BUILD` environment variable (default: `false`): + + * `false` (BUNDLED) — uploads `assets/echo-agent-prebuilt.zip`, which + bundles the agent source plus a `packages/` folder with Linux-built + dependencies, so the service skips pip entirely. + * `true` (REMOTE_BUILD) — uploads `assets/echo-agent.zip`, which contains + only the agent source plus `requirements.txt`; the service resolves + dependencies remotely from the public package index. + + The agent must already exist; create it with + `samples/hosted_agents/sample_create_hosted_agent_async.py`. + +USAGE: + python sample_create_hosted_agent_from_code_async.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" aiohttp 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 Hosted Agent name. Must already exist. + 3) AZURE_SUBSCRIPTION_ID - Azure subscription ID where the Azure AI account + and project are deployed. + 4) FOUNDRY_HOSTED_AGENT_REMOTE_BUILD - Optional. Set to `true` to use + REMOTE_BUILD; defaults to `false` (BUNDLED). +""" + +import asyncio +import hashlib +import os +import tempfile +from pathlib import Path + +from dotenv import load_dotenv + +from azure.identity.aio import DefaultAzureCredential + +from azure.ai.projects.aio import AIProjectClient +from azure.ai.projects.models import ( + CodeConfiguration, + CreateAgentVersionFromCodeContent, + CreateAgentVersionFromCodeMetadata, + HostedAgentDefinition, + ProtocolVersionRecord, +) + +from hosted_agents_util import select_echo_agent_code_zip, wait_for_agent_version_active_async +from rbac_util import ensure_agent_identity_rbac_async + + +async def main() -> None: + load_dotenv() + + endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] + subscription_id = os.environ["AZURE_SUBSCRIPTION_ID"] + use_remote_build = os.environ.get("FOUNDRY_HOSTED_AGENT_REMOTE_BUILD", "false").strip().lower() == "true" + + dependency_resolution, zip_filename, code_zip_bytes, code_zip_sha256 = select_echo_agent_code_zip(use_remote_build) + + async with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client, + ): + content = CreateAgentVersionFromCodeContent( + metadata=CreateAgentVersionFromCodeMetadata( + description=f"Code-based hosted agent uploaded with dependency_resolution={dependency_resolution.value}.", + definition=HostedAgentDefinition( + cpu="0.5", + memory="1Gi", + code_configuration=CodeConfiguration( + runtime="python_3_12", + entry_point=["python", "main.py"], + dependency_resolution=dependency_resolution, + ), + protocol_versions=[ProtocolVersionRecord(protocol="responses", version="1.0.0")], + ), + ), + code=(zip_filename, code_zip_bytes, "application/zip"), + ) + + created = await project_client.beta.agents.create_version_from_code( + agent_name=agent_name, + content=content, + code_zip_sha256=code_zip_sha256, + ) + print(f"Created code-based hosted agent version: {created.version}") + + await wait_for_agent_version_active_async( + project_client=project_client, + agent_name=agent_name, + agent_version=created.version, + ) + + # ensure_agent_identity_rbac_async uses async ARM management clients with the + # same async credential. + await ensure_agent_identity_rbac_async( + agent=created, + credential=credential, + subscription_id=subscription_id, + foundry_project_endpoint=endpoint, + ) + + # Download the zip for the version we just created, streaming to a temp file. + version_zip_path = Path(tempfile.gettempdir()) / f"{agent_name}-{created.version}.zip" + sha = hashlib.sha256() + version_stream = await project_client.beta.agents.download_code( + agent_name=agent_name, + agent_version=created.version, + ) + with open(version_zip_path, "wb") as f: + async for chunk in version_stream: + f.write(chunk) + sha.update(chunk) + downloaded_version_sha256 = sha.hexdigest() + print( + f"Downloaded version code zip to {version_zip_path}: {version_zip_path.stat().st_size} bytes, " + f"sha256={downloaded_version_sha256} (matches uploaded: {downloaded_version_sha256 == code_zip_sha256})" + ) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_session_log_stream.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_session_log_stream.py index e2658265cbb7..8957839ec8f9 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_session_log_stream.py +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_session_log_stream.py @@ -6,31 +6,34 @@ """ DESCRIPTION: - This sample demonstrates how to stream hosted agent session logs - using `project_client.beta.agents.get_session_log_stream` with the - synchronous AIProjectClient. + This sample demonstrates how to stream hosted agent session logs + using `project_client.beta.agents.get_session_log_stream` with the + synchronous AIProjectClient. - Sessions only work with Hosted Agents. + Sessions only work with Hosted Agents. - Session and log stream operations are currently preview features. - In the Python SDK, you access these operations via - `project_client.beta.agents`. + Session and log stream operations are currently preview features. + In the Python SDK, you access these operations via + `project_client.beta.agents`. USAGE: - python sample_session_log_stream.py + python sample_session_log_stream.py - Before running the sample: + Before running the sample: - pip install "azure-ai-projects>=2.1.0" python-dotenv + pip install "azure-ai-projects>=2.1.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_AGENT_CONTAINER_IMAGE - The Hosted Agent container image in the format '/[:|@]' + 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. + + If you don't have a Hosted Agent, run `sample_create_hosted_agent.py` or + `sample_create_hosted_agent_from_code.py` first to create one as a prerequisite. + + NOTE: This sample assumes the Foundry project and Azure AI account are in the + same resource group. - You can build and push an example image from - `samples/hosted_agents/assets/responses-echo-agent` and use that image value - for `FOUNDRY_AGENT_CONTAINER_IMAGE`. """ import os @@ -41,18 +44,18 @@ from azure.ai.projects import AIProjectClient from azure.ai.projects.models import ( - AgentEndpoint, + AgentEndpointConfig, AgentEndpointProtocol, FixedRatioVersionSelectionRule, VersionSelector, ) -from hosted_agents_util import create_agent_and_session +from azure.ai.projects.models import VersionRefIndicator +from hosted_agents_util import get_latest_active_agent_version load_dotenv() endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] -image = os.environ["FOUNDRY_AGENT_CONTAINER_IMAGE"] -agent_name = "MySessionHostedAgent" +agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] def _iter_sse_frames(stream, max_log_events: int): @@ -91,25 +94,32 @@ def _iter_sse_frames(stream, max_log_events: int): credential=credential, allow_preview=True, ) as project_client, - create_agent_and_session(project_client, agent_name, image) as (agent, session), + project_client.get_openai_client(agent_name=agent_name) as openai_client, ): - endpoint_config = AgentEndpoint( - version_selector=VersionSelector( - version_selection_rules=[ - FixedRatioVersionSelectionRule(agent_version=agent.version, traffic_percentage=100), - ] - ), - protocols=[AgentEndpointProtocol.RESPONSES], - ) - - project_client.beta.agents.patch_agent_details( + agent = get_latest_active_agent_version(project_client, agent_name) + session = project_client.beta.agents.create_session( agent_name=agent_name, - agent_endpoint=endpoint_config, + version_indicator=VersionRefIndicator(agent_version=agent.version), ) - print(f"Agent endpoint configured for agent: {agent_name}") - input_text = "Say hello in one short sentence." + print(f"Session created (id: {session.agent_session_id}, status: {session.status})") + try: + endpoint_config = AgentEndpointConfig( + version_selector=VersionSelector( + version_selection_rules=[ + FixedRatioVersionSelectionRule(agent_version=agent.version, traffic_percentage=100), + ] + ), + protocols=[AgentEndpointProtocol.RESPONSES], + ) + + project_client.beta.agents.patch_agent_details( + agent_name=agent_name, + agent_endpoint=endpoint_config, + ) + + print(f"Agent endpoint configured for agent: {agent_name}") + input_text = "Say hello in one short sentence." - with project_client.get_openai_client(agent_name=agent_name) as openai_client: response = openai_client.responses.create( input=input_text, extra_body={ @@ -118,12 +128,18 @@ def _iter_sse_frames(stream, max_log_events: int): ) print(f"Response output: {response.output_text}") - print("Streaming session logs...") - raw_stream = project_client.beta.agents.get_session_log_stream( - agent_name=agent_name, - agent_version=agent.version, - session_id=session.agent_session_id, - ) - for frame in _iter_sse_frames(raw_stream, max_log_events=30): - print(f"SSE event: {frame.get('event')}") - print(f"SSE data: {frame.get('data')}\n") + print("Streaming session logs...") + raw_stream = project_client.beta.agents.get_session_log_stream( + agent_name=agent_name, + agent_version=agent.version, + session_id=session.agent_session_id, + ) + for frame in _iter_sse_frames(raw_stream, max_log_events=30): + print(f"SSE event: {frame.get('event')}") + print(f"SSE data: {frame.get('data')}\n") + finally: + project_client.beta.agents.delete_session( + agent_name=agent_name, + session_id=session.agent_session_id, + ) + print(f"Session deleted (id: {session.agent_session_id})") diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_session_log_stream_async.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_session_log_stream_async.py index aeafaf39b7ae..3c2fe3bfa3d9 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_session_log_stream_async.py +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_session_log_stream_async.py @@ -26,11 +26,14 @@ 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_AGENT_CONTAINER_IMAGE - The Hosted Agent container image in the format '/[:|@]' + 2) FOUNDRY_HOSTED_AGENT_NAME - The name of an existing Hosted Agent. + + If you don't have a Hosted Agent, run `sample_create_hosted_agent_async.py` or + `sample_create_hosted_agent_from_code_async.py` first to create one as a prerequisite. + + NOTE: This sample assumes the Foundry project and Azure AI account are in the + same resource group. - You can build and push an example image from - `samples/hosted_agents/assets/responses-echo-agent` and use that image value - for `FOUNDRY_AGENT_CONTAINER_IMAGE`. """ import asyncio @@ -42,21 +45,47 @@ from azure.ai.projects.aio import AIProjectClient from azure.ai.projects.models import ( - AgentEndpoint, + AgentEndpointConfig, AgentEndpointProtocol, FixedRatioVersionSelectionRule, VersionSelector, ) -from hosted_agents_util import create_agent_and_session_async +from azure.ai.projects.models import VersionRefIndicator +from hosted_agents_util import get_latest_active_agent_version_async load_dotenv() -endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] -image = os.environ["FOUNDRY_AGENT_CONTAINER_IMAGE"] -agent_name = "MySessionHostedAgent" + +def _iter_sse_frames(stream, max_log_events: int): + event_count = 0 + buffer = "" + + for chunk in stream: + buffer += chunk.decode("utf-8", errors="replace") + + while "\n\n" in buffer: + frame, buffer = buffer.split("\n\n", 1) + event_name = None + data_lines = [] + + for line in frame.splitlines(): + if line.startswith("event: "): + event_name = line[7:] + elif line.startswith("data: "): + data_lines.append(line[6:]) + + if data_lines or event_name: + event_count += 1 + yield { + "event": event_name, + "data": "\n".join(data_lines), + } + + if event_count >= max_log_events: + return -async def _iter_sse_frames(stream, max_log_events: int): +async def _iter_sse_frames_async(stream, max_log_events: int): event_count = 0 buffer = "" @@ -85,7 +114,10 @@ async def _iter_sse_frames(stream, max_log_events: int): return -async def main() -> None: +async def main(): + endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] + async with ( DefaultAzureCredential() as credential, AIProjectClient( @@ -93,43 +125,55 @@ async def main() -> None: credential=credential, allow_preview=True, ) as project_client, - create_agent_and_session_async(project_client, agent_name, image) as (agent_version, session_id), ): - endpoint_config = AgentEndpoint( - version_selector=VersionSelector( - version_selection_rules=[ - FixedRatioVersionSelectionRule(agent_version=agent_version, traffic_percentage=100), - ] - ), - protocols=[AgentEndpointProtocol.RESPONSES], - ) - - await project_client.beta.agents.patch_agent_details( + agent = await get_latest_active_agent_version_async(project_client, agent_name) + session = await project_client.beta.agents.create_session( agent_name=agent_name, - agent_endpoint=endpoint_config, + version_indicator=VersionRefIndicator(agent_version=agent.version), ) - print(f"Agent endpoint configured for agent: {agent_name}") - input_text = "Say hello in one short sentence." + print(f"Session created (id: {session.agent_session_id}, status: {session.status})") + try: + endpoint_config = AgentEndpointConfig( + version_selector=VersionSelector( + version_selection_rules=[ + FixedRatioVersionSelectionRule(agent_version=agent.version, traffic_percentage=100), + ] + ), + protocols=[AgentEndpointProtocol.RESPONSES], + ) + + await project_client.beta.agents.patch_agent_details( + agent_name=agent_name, + agent_endpoint=endpoint_config, + ) + + print(f"Agent endpoint configured for agent: {agent_name}") + input_text = "Say hello in one short sentence." - async with project_client.get_openai_client(agent_name=agent_name) as openai_client: + openai_client = project_client.get_openai_client(agent_name=agent_name) response = await openai_client.responses.create( input=input_text, extra_body={ - "agent_session_id": session_id, + "agent_session_id": session.agent_session_id, }, ) print(f"Response output: {response.output_text}") - print("Streaming session logs...") - raw_stream = await project_client.beta.agents.get_session_log_stream( - agent_name=agent_name, - agent_version=agent_version, - session_id=session_id, - ) - - async for frame in _iter_sse_frames(raw_stream, max_log_events=30): - print(f"SSE event: {frame.get('event')}") - print(f"SSE data: {frame.get('data')}\n") + print("Streaming session logs...") + raw_stream = await project_client.beta.agents.get_session_log_stream( + agent_name=agent_name, + agent_version=agent.version, + session_id=session.agent_session_id, + ) + async for frame in _iter_sse_frames_async(raw_stream, max_log_events=30): + print(f"SSE event: {frame.get('event')}") + print(f"SSE data: {frame.get('data')}\n") + finally: + await project_client.beta.agents.delete_session( + agent_name=agent_name, + session_id=session.agent_session_id, + ) + print(f"Session deleted (id: {session.agent_session_id})") if __name__ == "__main__": diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_crud.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_crud.py index 5a840d7d0df1..5ef49160a3fb 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_crud.py +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_crud.py @@ -24,11 +24,17 @@ 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_AGENT_CONTAINER_IMAGE - The Hosted Agent container image in the format '/[:|@]' + 2) FOUNDRY_HOSTED_AGENT_NAME - The name of an existing Hosted Agent. - You can build and push an example image from - `samples/hosted_agents/assets/responses-echo-agent` and use that image value - for `FOUNDRY_AGENT_CONTAINER_IMAGE`. + If you don't have a Hosted Agent, run `sample_create_hosted_agent.py` or + `sample_create_hosted_agent_from_code.py` first to create one as a prerequisite. + +SDK FUNCTIONS: + - project_client.agents.list_versions: resolves the active version for the existing hosted agent. + - project_client.beta.agents.create_session: creates a session for the agent. + - project_client.beta.agents.get_session: retrieves a session by ID. + - project_client.beta.agents.list_sessions: lists sessions for an agent. + - project_client.beta.agents.delete_session: deletes a session by ID. """ import os @@ -38,13 +44,13 @@ from azure.identity import DefaultAzureCredential from azure.ai.projects import AIProjectClient -from azure.ai.projects.models import HostedAgentDefinition, VersionRefIndicator, ProtocolVersionRecord -from hosted_agents_util import wait_for_agent_version_active +from azure.ai.projects.models import VersionRefIndicator +from hosted_agents_util import get_latest_active_agent_version load_dotenv() endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] -image = os.environ["FOUNDRY_AGENT_CONTAINER_IMAGE"] +agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] with ( DefaultAzureCredential() as credential, @@ -54,39 +60,12 @@ allow_preview=True, ) as project_client, ): - agent_name = "MySessionHostedAgent" - - # Create an agent version to back the session - agent = project_client.agents.create_version( - agent_name=agent_name, - definition=HostedAgentDefinition( - cpu="0.5", - memory="1Gi", - image=image, - container_protocol_versions=[ - ProtocolVersionRecord(protocol="responses", version="1.0.0"), - ], - ), - metadata={"enableVnextExperience": "true"}, - ) - print(f"Agent created (name: {agent.name}, version: {agent.version})") - - wait_for_agent_version_active( - project_client=project_client, - agent_name=agent_name, - agent_version=agent.version, - ) - - isolation_key = "sample-isolation-key" - - # Create a session for the agent - print(f"Creating {3} sessions for the agent...") + agent = get_latest_active_agent_version(project_client, agent_name) session = project_client.beta.agents.create_session( agent_name=agent_name, - isolation_key=isolation_key, version_indicator=VersionRefIndicator(agent_version=agent.version), ) - print(f"Session created (id: {session.agent_session_id}, status: {session.status})") + print(f"Created session (id: {session.agent_session_id}, status: {session.status})") # Retrieve the session by its ID fetched = project_client.beta.agents.get_session( @@ -102,11 +81,8 @@ for item in sessions: print(f" - {item.agent_session_id} (status: {item.status})") - # Delete the session - print(f"Deleting session with id: {session.agent_session_id}...") project_client.beta.agents.delete_session( agent_name=agent_name, session_id=session.agent_session_id, - isolation_key=isolation_key, ) - print(f"Session with id: {session.agent_session_id} deleted.") + print(f"Deleted session (id: {session.agent_session_id})") diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_crud_async.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_crud_async.py index 49169702195b..82362b933488 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_crud_async.py +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_crud_async.py @@ -24,11 +24,17 @@ 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_AGENT_CONTAINER_IMAGE - The Hosted Agent container image in the format '/[:|@]' + 2) FOUNDRY_HOSTED_AGENT_NAME - The name of an existing Hosted Agent. - You can build and push an example image from - `samples/hosted_agents/assets/responses-echo-agent` and use that image value - for `FOUNDRY_AGENT_CONTAINER_IMAGE`. + If you don't have a Hosted Agent, run `sample_create_hosted_agent_async.py` or + `sample_create_hosted_agent_from_code_async.py` first to create one as a prerequisite. + +SDK FUNCTIONS: + - project_client.agents.list_versions: resolves the active version for the existing hosted agent. + - project_client.beta.agents.create_session: creates a session for the agent. + - project_client.beta.agents.get_session: retrieves a session by ID. + - project_client.beta.agents.list_sessions: lists sessions for an agent. + - project_client.beta.agents.delete_session: deletes a session by ID. """ import asyncio @@ -39,16 +45,16 @@ from azure.identity.aio import DefaultAzureCredential from azure.ai.projects.aio import AIProjectClient -from azure.ai.projects.models import HostedAgentDefinition, ProtocolVersionRecord, VersionRefIndicator -from hosted_agents_util import wait_for_agent_version_active_async +from azure.ai.projects.models import VersionRefIndicator +from hosted_agents_util import get_latest_active_agent_version_async load_dotenv() -endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] -image = os.environ["FOUNDRY_AGENT_CONTAINER_IMAGE"] +async def main(): + endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] -async def main() -> None: async with ( DefaultAzureCredential() as credential, AIProjectClient( @@ -57,62 +63,32 @@ async def main() -> None: allow_preview=True, ) as project_client, ): - agent_name = "MySessionHostedAgent" - - # Create an agent version to back the session. - agent = await project_client.agents.create_version( + agent = await get_latest_active_agent_version_async(project_client, agent_name) + session = await project_client.beta.agents.create_session( agent_name=agent_name, - definition=HostedAgentDefinition( - cpu="0.5", - memory="1Gi", - image=image, - container_protocol_versions=[ - ProtocolVersionRecord(protocol="responses", version="v1"), - ], - ), - metadata={"enableVnextExperience": "true"}, + version_indicator=VersionRefIndicator(agent_version=agent.version), ) - print(f"Agent created (name: {agent.name}, version: {agent.version})") + print(f"Created session (id: {session.agent_session_id}, status: {session.status})") - await wait_for_agent_version_active_async( - project_client=project_client, + # Retrieve the session by its ID + fetched = await project_client.beta.agents.get_session( agent_name=agent_name, - agent_version=agent.version, + session_id=session.agent_session_id, ) + print(f"Retrieved session (id: {fetched.agent_session_id}, status: {fetched.status})") - isolation_key = "sample-isolation-key" - session = await project_client.beta.agents.create_session( + # List sessions for the agent + print("Listing sessions for the agent...") + sessions = project_client.beta.agents.list_sessions(agent_name=agent_name) + print("Sessions:") + async for item in sessions: + print(f" - {item.agent_session_id} (status: {item.status})") + + await project_client.beta.agents.delete_session( agent_name=agent_name, - isolation_key=isolation_key, - version_indicator=VersionRefIndicator(agent_version=agent.version), + session_id=session.agent_session_id, ) - print(f"Session created (id: {session.agent_session_id}, status: {session.status})") - - try: - fetched = await project_client.beta.agents.get_session( - agent_name=agent_name, - session_id=session.agent_session_id, - ) - print(f"Retrieved session (id: {fetched.agent_session_id}, status: {fetched.status})") - - print("Listing sessions for the agent...") - sessions = [] - async for item in project_client.beta.agents.list_sessions(agent_name=agent_name): - sessions.append(item) - print("Sessions:") - for item in sessions: - print(f" - {item.agent_session_id} (status: {item.status})") - finally: - print(f"Deleting session with id: {session.agent_session_id}...") - await project_client.beta.agents.delete_session( - agent_name=agent_name, - session_id=session.agent_session_id, - isolation_key=isolation_key, - ) - print(f"Session with id: {session.agent_session_id} deleted.") - - await project_client.agents.delete_version(agent_name=agent_name, agent_version=agent.version) - print(f"Agent version {agent.version} deleted.") + print(f"Deleted session (id: {session.agent_session_id})") if __name__ == "__main__": diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_files_upload_download.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_files_upload_download.py index fa423e02c28c..3bc0a932d890 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_files_upload_download.py +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_files_upload_download.py @@ -24,11 +24,10 @@ 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_AGENT_CONTAINER_IMAGE - The Hosted Agent container image in the format '/[:|@]' + 2) FOUNDRY_HOSTED_AGENT_NAME - The name of an existing Hosted Agent. - You can build and push an example image from - `samples/hosted_agents/assets/responses-echo-agent` and use that image value - for `FOUNDRY_AGENT_CONTAINER_IMAGE`. + If you don't have a Hosted Agent, run `sample_create_hosted_agent.py` or + `sample_create_hosted_agent_from_code.py` first to create one as a prerequisite. """ import os @@ -38,12 +37,13 @@ from azure.identity import DefaultAzureCredential from azure.ai.projects import AIProjectClient -from hosted_agents_util import create_agent_and_session +from azure.ai.projects.models import VersionRefIndicator +from hosted_agents_util import get_latest_active_agent_version load_dotenv() endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] -image = os.environ["FOUNDRY_AGENT_CONTAINER_IMAGE"] +agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] # Construct the paths to the data folder and data file used in this sample script_dir = os.path.dirname(os.path.abspath(__file__)) @@ -62,10 +62,13 @@ allow_preview=True, ) as project_client, ): - agent_name = "MySessionHostedAgent" - - with create_agent_and_session(project_client, agent_name, image) as (_, session): - + agent = get_latest_active_agent_version(project_client, agent_name) + session = project_client.beta.agents.create_session( + agent_name=agent_name, + version_indicator=VersionRefIndicator(agent_version=agent.version), + ) + print(f"Session created (id: {session.agent_session_id}, status: {session.status})") + try: # Upload and list session files project_client.beta.agents.upload_session_file( agent_name=agent_name, @@ -115,3 +118,9 @@ agent_session_id=session.agent_session_id, path=remote_file_path2, ) + finally: + project_client.beta.agents.delete_session( + agent_name=agent_name, + session_id=session.agent_session_id, + ) + print(f"Session deleted (id: {session.agent_session_id})") diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_files_upload_download_async.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_files_upload_download_async.py index 0f37627826d1..cc9ad46eb64f 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_files_upload_download_async.py +++ b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_sessions_files_upload_download_async.py @@ -6,8 +6,8 @@ """ DESCRIPTION: - This sample demonstrates how to perform session file upload, list, download, - and delete operations using the asynchronous AIProjectClient. + This sample demonstrates how to perform CRUD operations on agent Sessions + using the asynchronous AIProjectClient. Sessions only work with Hosted Agents. @@ -24,11 +24,10 @@ 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_AGENT_CONTAINER_IMAGE - The Hosted Agent container image in the format '/[:|@]' + 2) FOUNDRY_HOSTED_AGENT_NAME - The name of an existing Hosted Agent. - You can build and push an example image from - `samples/hosted_agents/assets/responses-echo-agent` and use that image value - for `FOUNDRY_AGENT_CONTAINER_IMAGE`. + If you don't have a Hosted Agent, run `sample_create_hosted_agent_async.py` or + `sample_create_hosted_agent_from_code_async.py` first to create one as a prerequisite. """ import asyncio @@ -39,23 +38,24 @@ from azure.identity.aio import DefaultAzureCredential from azure.ai.projects.aio import AIProjectClient -from hosted_agents_util import create_agent_and_session_async +from azure.ai.projects.models import VersionRefIndicator +from hosted_agents_util import get_latest_active_agent_version_async load_dotenv() -endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] -image = os.environ["FOUNDRY_AGENT_CONTAINER_IMAGE"] -# Construct the paths to the data folder and data file used in this sample -script_dir = os.path.dirname(os.path.abspath(__file__)) -data_folder = os.path.join(script_dir, "assets") -data_file1 = os.path.join(data_folder, "data_file1.txt") -data_file2 = os.path.join(data_folder, "data_file2.txt") -remote_file_path1 = "/remote/data_file1.txt" -remote_file_path2 = "/remote/data_file2.txt" +async def main(): + endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] + # Construct the paths to the data folder and data file used in this sample + script_dir = os.path.dirname(os.path.abspath(__file__)) + data_folder = os.path.join(script_dir, "assets") + data_file1 = os.path.join(data_folder, "data_file1.txt") + data_file2 = os.path.join(data_folder, "data_file2.txt") + remote_file_path1 = "/remote/data_file1.txt" + remote_file_path2 = "/remote/data_file2.txt" -async def main() -> None: async with ( DefaultAzureCredential() as credential, AIProjectClient( @@ -64,12 +64,17 @@ async def main() -> None: allow_preview=True, ) as project_client, ): - agent_name = "MySessionHostedAgent" - - async with create_agent_and_session_async(project_client, agent_name, image) as (_, session_id): + agent = await get_latest_active_agent_version_async(project_client, agent_name) + session = await project_client.beta.agents.create_session( + agent_name=agent_name, + version_indicator=VersionRefIndicator(agent_version=agent.version), + ) + print(f"Session created (id: {session.agent_session_id}, status: {session.status})") + try: + # Upload and list session files await project_client.beta.agents.upload_session_file( agent_name=agent_name, - session_id=session_id, + session_id=session.agent_session_id, content_or_file_path=data_file1, path=remote_file_path1, ) @@ -77,7 +82,7 @@ async def main() -> None: print(f"Uploading session file: {data_file2} -> {remote_file_path2}") await project_client.beta.agents.upload_session_file( agent_name=agent_name, - session_id=session_id, + session_id=session.agent_session_id, content_or_file_path=data_file2, path=remote_file_path2, ) @@ -85,35 +90,42 @@ async def main() -> None: print("Listing session files for the session at path '.'...") files = await project_client.beta.agents.get_session_files( agent_name=agent_name, - agent_session_id=session_id, + agent_session_id=session.agent_session_id, path="/remote", ) for entry in files.entries: print(f" - name={entry.name}, size={entry.size}, is_directory={entry.is_directory}") print(f"Downloading and printing content from '{remote_file_path1}'") - download_stream = await project_client.beta.agents.download_session_file( + content_bytes = b"" + async for chunk in await project_client.beta.agents.download_session_file( agent_name=agent_name, - agent_session_id=session_id, + agent_session_id=session.agent_session_id, path=remote_file_path1, - ) - content_bytes = b"".join([chunk async for chunk in download_stream]) + ): + content_bytes += chunk file_content = content_bytes.decode("utf-8", errors="replace") print(f"Session file content ({remote_file_path1}):\n{file_content}") print(f"Deleting session file at path: {remote_file_path1}...") await project_client.beta.agents.delete_session_file( agent_name=agent_name, - agent_session_id=session_id, + agent_session_id=session.agent_session_id, path=remote_file_path1, ) print(f"Deleting session file at path: {remote_file_path2}...") await project_client.beta.agents.delete_session_file( agent_name=agent_name, - agent_session_id=session_id, + agent_session_id=session.agent_session_id, path=remote_file_path2, ) + finally: + await project_client.beta.agents.delete_session( + agent_name=agent_name, + session_id=session.agent_session_id, + ) + print(f"Session deleted (id: {session.agent_session_id})") if __name__ == "__main__": diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_crud.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_crud.py deleted file mode 100644 index 4b7bb4984aad..000000000000 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_crud.py +++ /dev/null @@ -1,82 +0,0 @@ -# pylint: disable=line-too-long,useless-suppression -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -""" -DESCRIPTION: - This sample demonstrates how to perform CRUD operations on Skills - using the synchronous AIProjectClient. - - Skills are a preview feature. In the Python SDK, you access - these operations via `project_client.beta.skills`. - -USAGE: - python sample_skills_crud.py - - Before running the sample: - - pip install "azure-ai-projects>=2.1.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. -""" - -import os - -from dotenv import load_dotenv - -from azure.core.exceptions import HttpResponseError, ResourceNotFoundError -from azure.identity import DefaultAzureCredential - -from azure.ai.projects import AIProjectClient - -load_dotenv() - -endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] - -with ( - DefaultAzureCredential() as credential, - AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client, -): - skills_client = project_client.beta.skills - - skill_name = "product_support_skill" - - try: - skills_client.delete(skill_name) - print(f"Skill `{skill_name}` deleted") - except ResourceNotFoundError: - pass - - created = skills_client.create( - name=skill_name, - description="Example skill created by the azure-ai-projects sample.", - instructions="You help answer product support questions using company policy and product guidance.", - metadata={"status": "created", "domain": "support"}, - ) - print( - f"Created skill: {created.name} ({created.skill_id}) " - f"has_blob={created.has_blob} metadata={created.metadata}" - ) - - fetched = skills_client.get(skill_name) - print(f"Retrieved skill: {fetched.name} ({fetched.skill_id}) " f"description={fetched.description!r}") - - updated = skills_client.update( - skill_name, - description="Updated description for the sample skill.", - metadata={"status": "updated", "domain": "support"}, - ) - print( - f"Updated skill: {updated.name} ({updated.skill_id}) " - f"has_blob={updated.has_blob} metadata={updated.metadata}" - ) - - skills = list(skills_client.list()) - print(f"Found {len(skills)} skills or more") - - deleted = skills_client.delete(skill_name) - print(f"Deleted skill: {deleted}") diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_crud_async.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_crud_async.py deleted file mode 100644 index 8f93b72d85dd..000000000000 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_crud_async.py +++ /dev/null @@ -1,94 +0,0 @@ -# pylint: disable=line-too-long,useless-suppression -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -""" -DESCRIPTION: - This sample demonstrates how to perform CRUD operations on Skills - using the asynchronous AIProjectClient. - - Skills are a preview feature. In the Python SDK, you access - these operations via `project_client.beta.skills`. - -USAGE: - python sample_skills_crud_async.py - - Before running the sample: - - pip install "azure-ai-projects>=2.1.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. -""" - -import os - -from dotenv import load_dotenv - -from azure.core.exceptions import HttpResponseError, ResourceNotFoundError -from azure.identity.aio import DefaultAzureCredential - -from azure.ai.projects.aio import AIProjectClient - -load_dotenv() - -endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] - - -async def main() -> None: - async with ( - DefaultAzureCredential() as credential, - AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client, - ): - skills_client = project_client.beta.skills - - skill_name = "product_support_skill" - - try: - await skills_client.delete(skill_name) - print(f"Skill `{skill_name}` deleted") - except ResourceNotFoundError: - pass - - created = None - created = await skills_client.create( - name=skill_name, - description="Example skill created by the azure-ai-projects sample.", - instructions="You help answer product support questions using company policy and product guidance.", - metadata={"status": "created", "domain": "support"}, - ) - print( - f"Created skill: {created.name} ({created.skill_id}) " - f"has_blob={created.has_blob} metadata={created.metadata}" - ) - - fetched = await skills_client.get(skill_name) - print(f"Retrieved skill: {fetched.name} ({fetched.skill_id}) " f"description={fetched.description!r}") - - updated = await skills_client.update( - skill_name, - description="Updated description for the sample skill.", - instructions="You help answer product support questions and escalate billing issues.", - metadata={"status": "updated", "domain": "support"}, - ) - print( - f"Updated skill: {updated.name} ({updated.skill_id}) " - f"has_blob={updated.has_blob} metadata={updated.metadata}" - ) - - skills = [] - async for skill in skills_client.list(): - skills.append(skill) - print(f"Found {len(skills)} skills or more") - - deleted = await skills_client.delete(skill_name) - print(f"Deleted skill: {deleted}") - - -if __name__ == "__main__": - import asyncio - - asyncio.run(main()) diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_upload_and_download.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_upload_and_download.py deleted file mode 100644 index 3e32296fed23..000000000000 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_upload_and_download.py +++ /dev/null @@ -1,77 +0,0 @@ -# pylint: disable=line-too-long,useless-suppression -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -""" -DESCRIPTION: - This sample demonstrates uploading and downloading a skill package using the - synchronous AIProjectClient. - - 1) Delete an existing skill with the same name (if it exists). - 2) Upload a package with `create_from_package(...)`. - 3) Retrieve the uploaded skill with `get(...)`. - 4) Download the package with `download(...)` to the temp folder. - 5) Delete the uploaded skill. - - Skills are a preview feature. In the Python SDK, you access - these operations via `project_client.beta.skills`. - -USAGE: - python sample_skills_upload_and_download.py - - Before running the sample: - - pip install "azure-ai-projects>=2.1.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. - - This sample uploads `samples/hosted_agents/assets/canvas-design.zip`. -""" - -import os -import tempfile -from datetime import datetime -from pathlib import Path - -from dotenv import load_dotenv - -from azure.core.exceptions import ResourceNotFoundError -from azure.identity import DefaultAzureCredential - -from azure.ai.projects import AIProjectClient - -load_dotenv() - -endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] -download_folder = Path(tempfile.gettempdir()).resolve() -skill_name = "canvas-design" -skill_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "assets/canvas-design.zip")) -with ( - DefaultAzureCredential() as credential, - AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client, -): - - try: - project_client.beta.skills.delete(skill_name) - print(f"Skill `{skill_name}` deleted") - except ResourceNotFoundError: - pass - - imported = project_client.beta.skills.create_from_package(Path(skill_file_path).read_bytes()) - imported_skill_name = imported.name - print(f"Imported skill from package: {imported.name} ({imported.skill_id}) has_blob={imported.has_blob}") - - fetched = project_client.beta.skills.get(imported.name) - print(f"Fetched imported skill: {fetched.name} ({fetched.skill_id}) has_blob={fetched.has_blob}") - - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - download_path = download_folder / f"{fetched.name}_{timestamp}.zip" - download_path.write_bytes(b"".join(project_client.beta.skills.download(fetched.name))) - print(f"Downloaded skill package path: {download_path}") - - deleted = project_client.beta.skills.delete(fetched.name) - print(f"Deleted imported skill: {deleted}") diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_upload_and_download_async.py b/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_upload_and_download_async.py deleted file mode 100644 index a17addaf72f4..000000000000 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_skills_upload_and_download_async.py +++ /dev/null @@ -1,86 +0,0 @@ -# pylint: disable=line-too-long,useless-suppression -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -""" -DESCRIPTION: - This sample demonstrates uploading and downloading a skill package using the - asynchronous AIProjectClient. - - 1) Delete an existing skill with the same name (if it exists). - 2) Upload a package with `create_from_package(...)`. - 3) Retrieve the uploaded skill with `get(...)`. - 4) Download the package with `download(...)` to the temp folder. - 5) Delete the uploaded skill. - - Skills are a preview feature. In the Python SDK, you access - these operations via `project_client.beta.skills`. - -USAGE: - python sample_skills_upload_and_download_async.py - - Before running the sample: - - pip install "azure-ai-projects>=2.1.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. - - This sample uploads `samples/hosted_agents/assets/canvas-design.zip`. -""" - -import asyncio -import os -import tempfile -from datetime import datetime -from pathlib import Path - -from dotenv import load_dotenv - -from azure.core.exceptions import ResourceNotFoundError -from azure.identity.aio import DefaultAzureCredential - -from azure.ai.projects.aio import AIProjectClient - -load_dotenv() - -endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] -download_folder = Path(tempfile.gettempdir()).resolve() -skill_name = "canvas-design" -skill_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "assets/canvas-design.zip")) - - -async def main() -> None: - async with ( - DefaultAzureCredential() as credential, - AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client, - ): - - try: - await project_client.beta.skills.delete(skill_name) - print(f"Skill `{skill_name}` deleted") - except ResourceNotFoundError: - pass - - imported = await project_client.beta.skills.create_from_package(Path(skill_file_path).read_bytes()) - imported_skill_name = imported.name - print(f"Imported skill from package: {imported.name} ({imported.skill_id}) has_blob={imported.has_blob}") - - fetched = await project_client.beta.skills.get(imported.name) - print(f"Fetched imported skill: {fetched.name} ({fetched.skill_id}) has_blob={fetched.has_blob}") - - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - download_path = download_folder / f"{fetched.name}_{timestamp}.zip" - download_stream = await project_client.beta.skills.download(fetched.name) - download_path.write_bytes(b"".join([chunk async for chunk in download_stream])) - print(f"Downloaded skill package path: {download_path}") - - deleted = await project_client.beta.skills.delete(fetched.name) - print(f"Deleted imported skill: {deleted}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/sdk/ai/azure-ai-projects/samples/indexes/sample_indexes.py b/sdk/ai/azure-ai-projects/samples/indexes/sample_indexes.py index 04ac684cb8d3..e323d540c829 100644 --- a/sdk/ai/azure-ai-projects/samples/indexes/sample_indexes.py +++ b/sdk/ai/azure-ai-projects/samples/indexes/sample_indexes.py @@ -45,7 +45,6 @@ project_client.get_openai_client() as openai_client, ): - # [START indexes_sample] print(f"Create Index `{index_name}` with version `{index_version}`, referencing an existing AI Search resource:") index = project_client.indexes.create_or_update( name=index_name, @@ -68,4 +67,3 @@ print(f"Delete Index `{index_name}` version `{index_version}`:") project_client.indexes.delete(name=index_name, version=index_version) - # [END indexes_sample] diff --git a/sdk/ai/azure-ai-projects/samples/models/sample_models_basic.py b/sdk/ai/azure-ai-projects/samples/models/sample_models_basic.py new file mode 100644 index 000000000000..274255c046e2 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/models/sample_models_basic.py @@ -0,0 +1,123 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Given an AIProjectClient, this sample demonstrates how to use the synchronous + `.beta.models` operations to register a local model with a Microsoft Foundry + project, list and inspect model versions, retrieve storage credentials, + update version metadata, and delete a model version. + + The recommended entry point is the patched helper + `project_client.beta.models.create(...)`, which packs the spec's + three required steps (`pending_upload` -> `azcopy copy` -> `pending_create_version`) + into a single call and polls until the new ModelVersion is observable. + +USAGE: + python sample_models_basic.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity python-dotenv + + AzCopy must also be installed and on PATH (used by `create` to + upload weight files): + + winget install --id Microsoft.Azure.AZCopy.10 -e + + See https://aka.ms/downloadazcopy for other platforms. + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as + found in the overview page of your Microsoft Foundry project. + 2) MODEL_NAME - Optional. The name of the model to register. Defaults to + "sample-model". + 3) MODEL_VERSION - Optional. The version of the model to register. + Defaults to "1". + 4) DATA_FOLDER - Optional. The folder containing the local weight files + to upload. Defaults to a temp folder created with two tiny dummy files. +""" + +import os +import pathlib +import tempfile + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential + +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import ( + FoundryModelWeightType, + ModelCredentialRequest, + UpdateModelVersionRequest, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_name = os.environ.get("MODEL_NAME", "sample-model") +model_version = os.environ.get("MODEL_VERSION", "1") + +# Construct the path to the local folder of weight files used in this sample. +data_folder = os.environ.get("DATA_FOLDER") +if not data_folder: + data_folder = tempfile.mkdtemp(prefix="sample-model-") + (pathlib.Path(data_folder) / "weights.bin").write_bytes(b"hello-foundry-model") + (pathlib.Path(data_folder) / "config.json").write_text('{"sample": true}') + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, +): + + print( + f"Register a local model named `{model_name}` version `{model_version}` " + f"by uploading the contents of `{data_folder}` via `create`." + ) + model = project_client.beta.models.create( + name=model_name, + version=model_version, + source=data_folder, + weight_type=FoundryModelWeightType.FULL_WEIGHT, + description="Sample model registered from sample_models_basic.py", + tags={"source": "sample_models_basic.py"}, + ) + print(f"Created (name: {model.name}, version: {model.version}, blob_uri: {model.blob_uri})") + + print(f"Get a specific model version `{model_name}`@`{model_version}`:") + fetched = project_client.beta.models.get(name=model_name, version=model_version) + print(f"Fetched (name: {fetched.name}, version: {fetched.version}, blob_uri: {fetched.blob_uri})") + + print(f"List all versions of model `{model_name}`:") + for mv in project_client.beta.models.list_versions(name=model_name): + print(f" - {mv.version}") + + print("List the latest version of every registered model in this project:") + for mv in project_client.beta.models.list(): + print(f" - {mv.name}@{mv.version}") + + print(f"Get blob credentials for `{model_name}`@`{model_version}`:") + creds = project_client.beta.models.get_credentials( + name=model_name, + version=model_version, + credential_request=ModelCredentialRequest(blob_uri=model.blob_uri), + ) + print(f"Credentials (type: {type(creds).__name__})") + + print(f"Update description and tags on `{model_name}`@`{model_version}`:") + updated = project_client.beta.models.update( + name=model_name, + version=model_version, + body=UpdateModelVersionRequest( + description="Updated description", + tags={"source": "sample_models_basic.py", "updated": "true"}, + ), + ) + print(f"Updated (name: {updated.name}, version: {updated.version}, description: {updated.description})") + + print(f"Delete the model version created above (`{model_name}`@`{model_version}`):") + project_client.beta.models.delete(name=model_name, version=model_version) diff --git a/sdk/ai/azure-ai-projects/samples/models/sample_models_basic_async.py b/sdk/ai/azure-ai-projects/samples/models/sample_models_basic_async.py new file mode 100644 index 000000000000..d0fd70bde4a5 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/models/sample_models_basic_async.py @@ -0,0 +1,177 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Given an asynchronous AIProjectClient, this sample demonstrates how to + register a local model with a Microsoft Foundry project and exercise the + asynchronous `.beta.models` operations: `pending_upload`, `pending_create_version`, + `get`, `list_versions`, `list`, `get_credentials`, `update`, and `delete`. + + The async client does not expose the `create` convenience helper + (which shells out to the synchronous `azcopy` CLI). This sample instead + drives the spec's three-step upload-first sequence directly: + + 1) `pending_upload(...)` -- the service provisions a project-managed + blob container and returns a SAS URI. + 2) Upload the local weight files to that SAS container using + `azure.storage.blob.aio.ContainerClient`. + 3) `pending_create_version(...)` -- commit the registration. The service returns + 202 Accepted and finalizes the ModelVersion asynchronously, so we + poll `get(...)` until the new version is observable. + +USAGE: + python sample_models_basic_async.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity azure-storage-blob aiohttp python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as + found in the overview page of your Microsoft Foundry project. + 2) MODEL_NAME - Optional. The name of the model to register. Defaults to + "sample-model-async". + 3) MODEL_VERSION - Optional. The version of the model to register. + Defaults to "1". + 4) DATA_FOLDER - Optional. The folder containing the local weight files + to upload. Defaults to a temp folder created with two tiny dummy files. +""" + +import asyncio +import os +import pathlib +import tempfile +import time + +from dotenv import load_dotenv + +from azure.core.exceptions import ResourceNotFoundError +from azure.identity.aio import DefaultAzureCredential +from azure.storage.blob.aio import ContainerClient + +from azure.ai.projects.aio import AIProjectClient +from azure.ai.projects.models import ( + FoundryModelWeightType, + ModelCredentialRequest, + ModelPendingUploadRequest, + ModelVersion, + PendingUploadType, + UpdateModelVersionRequest, +) + +load_dotenv() + + +async def main() -> None: + + endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + model_name = os.environ.get("MODEL_NAME", "sample-model-async") + model_version = os.environ.get("MODEL_VERSION", "1") + + # Construct the path to the local folder of weight files used in this sample. + data_folder = os.environ.get("DATA_FOLDER") + if not data_folder: + data_folder = tempfile.mkdtemp(prefix="sample-model-async-") + (pathlib.Path(data_folder) / "weights.bin").write_bytes(b"hello-foundry-model") + (pathlib.Path(data_folder) / "config.json").write_text('{"sample": true}') + source_dir = pathlib.Path(data_folder) + + async with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + ): + + print(f"Step 1/3: pending_upload(name=`{model_name}`, version=`{model_version}`)") + pending = await project_client.beta.models.pending_upload( + name=model_name, + version=model_version, + pending_upload_request=ModelPendingUploadRequest( + pending_upload_type=PendingUploadType.TEMPORARY_BLOB_REFERENCE, + ), + ) + # The wire payload uses the datastore-style `blobReferenceForConsumption` + # shape on some Foundry deployments and the SDK-modeled `blobReference` + # shape on others. Tolerate both. + payload = pending.as_dict() + blob_ref = payload.get("blobReferenceForConsumption") or payload.get("blobReference") or {} + sas_uri = (blob_ref.get("credential") or {}).get("sasUri") + container_blob_uri = blob_ref.get("blobUri") + if not sas_uri or not container_blob_uri: + raise RuntimeError(f"pending_upload response missing SAS / blob URI: {payload!r}") + print(f" blob_uri = {container_blob_uri}") + print(f" sas_uri = {sas_uri.split('?', 1)[0]}?") + + print(f"Step 2/3: upload contents of `{source_dir}` to the SAS container") + async with ContainerClient.from_container_url(sas_uri) as container_client: + for f in [p for p in source_dir.rglob("*") if p.is_file()]: + rel = f.relative_to(source_dir).as_posix() + with f.open("rb") as fp: + await container_client.upload_blob(name=rel, data=fp, overwrite=True) + print(f" uploaded {rel} ({f.stat().st_size} bytes)") + + print(f"Step 3/3: pending_create_version(name=`{model_name}`, version=`{model_version}`)") + await project_client.beta.models.pending_create_version( + name=model_name, + version=model_version, + model_version=ModelVersion( + blob_uri=container_blob_uri, + weight_type=FoundryModelWeightType.FULL_WEIGHT, + description="Sample model registered from sample_models_basic_async.py", + tags={"source": "sample_models_basic_async.py"}, + ), + ) + + # `pending_create_version` returns 202 Accepted; poll `get` until the committed + # ModelVersion is observable. + print(f"Polling get(`{model_name}`, `{model_version}`) until the ModelVersion is committed...") + deadline = time.monotonic() + 300.0 + model: ModelVersion + while True: + try: + model = await project_client.beta.models.get(name=model_name, version=model_version) + break + except ResourceNotFoundError: + if time.monotonic() >= deadline: + raise RuntimeError( + f"Model `{model_name}`@`{model_version}` did not appear within 300s after pending_create_version." + ) + await asyncio.sleep(2.0) + print(f"Created (name: {model.name}, version: {model.version}, blob_uri: {model.blob_uri})") + + print(f"List all versions of model `{model_name}`:") + async for mv in project_client.beta.models.list_versions(name=model_name): + print(f" - {mv.version}") + + print("List the latest version of every registered model in this project:") + async for mv in project_client.beta.models.list(): + print(f" - {mv.name}@{mv.version}") + + print(f"Get blob credentials for `{model_name}`@`{model_version}`:") + creds = await project_client.beta.models.get_credentials( + name=model_name, + version=model_version, + credential_request=ModelCredentialRequest(blob_uri=model.blob_uri), + ) + print(f"Credentials (type: {type(creds).__name__})") + + print(f"Update description and tags on `{model_name}`@`{model_version}`:") + updated = await project_client.beta.models.update( + name=model_name, + version=model_version, + body=UpdateModelVersionRequest( + description="Updated description", + tags={"source": "sample_models_basic_async.py", "updated": "true"}, + ), + ) + print(f"Updated (name: {updated.name}, version: {updated.version}, description: {updated.description})") + + print(f"Delete the model version created above (`{model_name}`@`{model_version}`):") + await project_client.beta.models.delete(name=model_name, version=model_version) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/sdk/ai/azure-ai-projects/samples/models/sample_models_create_and_poll.py b/sdk/ai/azure-ai-projects/samples/models/sample_models_create_and_poll.py new file mode 100644 index 000000000000..7e38fefa2038 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/models/sample_models_create_and_poll.py @@ -0,0 +1,141 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + Given an AIProjectClient, this sample demonstrates how to register a local + model with a Microsoft Foundry project WITHOUT relying on the + `create` helper or the `azcopy` CLI. It hand-rolls the spec's + three-step upload-first sequence using only the generated `.beta.models` + operations and `azure-storage-blob`: + + 1) `pending_upload(...)` -- the service provisions a project-managed + blob container and returns a SAS URI. + 2) Upload the local weight files directly to that SAS container using + `azure.storage.blob.ContainerClient`. + 3) `pending_create_version(...)` -- commit the registration. The service returns + 202 Accepted and finalizes the ModelVersion asynchronously, so we + poll `get(...)` until the new version is observable. + + This is useful when `azcopy` is not available, or when callers want to + integrate the upload step with their own progress reporting / retry logic. + +USAGE: + python sample_models_create_and_poll.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" azure-identity azure-storage-blob python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as + found in the overview page of your Microsoft Foundry project. + 2) MODEL_NAME - Optional. The name of the model to register. Defaults to + "sample-model-pending-upload". + 3) MODEL_VERSION - Optional. The version of the model to register. + Defaults to "1". + 4) DATA_FOLDER - Optional. The folder containing the local weight files + to upload. Defaults to a temp folder created with two tiny dummy files. +""" + +import os +import pathlib +import tempfile +import time + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential +from azure.core.exceptions import ResourceNotFoundError +from azure.storage.blob import ContainerClient + +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import ( + FoundryModelWeightType, + ModelPendingUploadRequest, + ModelVersion, + PendingUploadType, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +model_name = os.environ.get("MODEL_NAME", "sample-model-pending-upload") +model_version = os.environ.get("MODEL_VERSION", "1") + +# Construct the path to the local folder of weight files used in this sample. +data_folder = os.environ.get("DATA_FOLDER") +if not data_folder: + data_folder = tempfile.mkdtemp(prefix="sample-model-") + (pathlib.Path(data_folder) / "weights.bin").write_bytes(b"hello-foundry-model") + (pathlib.Path(data_folder) / "config.json").write_text('{"sample": true}') +source_dir = pathlib.Path(data_folder) + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, +): + + print(f"Step 1/3: pending_upload(name=`{model_name}`, version=`{model_version}`)") + pending = project_client.beta.models.pending_upload( + name=model_name, + version=model_version, + pending_upload_request=ModelPendingUploadRequest( + pending_upload_type=PendingUploadType.TEMPORARY_BLOB_REFERENCE, + ), + ) + # The wire payload uses the datastore-style `blobReferenceForConsumption` + # shape on some Foundry deployments and the SDK-modeled `blobReference` + # shape on others. Tolerate both. + payload = pending.as_dict() + blob_ref = payload.get("blobReferenceForConsumption") or payload.get("blobReference") or {} + sas_uri = (blob_ref.get("credential") or {}).get("sasUri") + container_blob_uri = blob_ref.get("blobUri") + if not sas_uri or not container_blob_uri: + raise RuntimeError(f"pending_upload response missing SAS / blob URI: {payload!r}") + print(f" blob_uri = {container_blob_uri}") + print(f" sas_uri = {sas_uri.split('?', 1)[0]}?") + + print(f"Step 2/3: upload contents of `{source_dir}` to the SAS container") + container_client = ContainerClient.from_container_url(sas_uri) + files = [p for p in source_dir.rglob("*") if p.is_file()] + for f in files: + rel = f.relative_to(source_dir).as_posix() + with f.open("rb") as fp: + container_client.upload_blob(name=rel, data=fp, overwrite=True) + print(f" uploaded {rel} ({f.stat().st_size} bytes)") + + print(f"Step 3/3: pending_create_version(name=`{model_name}`, version=`{model_version}`)") + project_client.beta.models.pending_create_version( + name=model_name, + version=model_version, + model_version=ModelVersion( + blob_uri=container_blob_uri, + weight_type=FoundryModelWeightType.FULL_WEIGHT, + description="Sample model registered from sample_models_pending_upload.py", + tags={"source": "sample_models_pending_upload.py"}, + ), + ) + + # `pending_create_version` returns 202 Accepted; poll `get` until the committed + # ModelVersion is observable. + print(f"Polling get(`{model_name}`, `{model_version}`) until the ModelVersion is committed...") + deadline = time.monotonic() + 300.0 + model = None + while True: + try: + model = project_client.beta.models.get(name=model_name, version=model_version) + break + except ResourceNotFoundError: + if time.monotonic() >= deadline: + raise RuntimeError( + f"Model `{model_name}`@`{model_version}` did not appear within 300s after pending_create_version." + ) + time.sleep(2.0) + print(model) + + print(f"Delete the model version created above (`{model_name}`@`{model_version}`):") + project_client.beta.models.delete(name=model_name, version=model_version) diff --git a/sdk/ai/azure-ai-projects/samples/red_team/sample_red_team.py b/sdk/ai/azure-ai-projects/samples/red_team/sample_red_team.py index eebf4356fee0..31f25a25636c 100644 --- a/sdk/ai/azure-ai-projects/samples/red_team/sample_red_team.py +++ b/sdk/ai/azure-ai-projects/samples/red_team/sample_red_team.py @@ -49,7 +49,6 @@ AIProjectClient(endpoint=endpoint, credential=credential) 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 @@ -81,4 +80,3 @@ print("Listing all Red Team scans") for scan in project_client.beta.red_teams.list(): print(f"Found scan: {scan.name}, Status: {scan.status}") - # [END red_team_sample] diff --git a/sdk/ai/azure-ai-projects/samples/red_team/sample_red_team_async.py b/sdk/ai/azure-ai-projects/samples/red_team/sample_red_team_async.py index cd7969dc6b9c..60201ef8fd7d 100644 --- a/sdk/ai/azure-ai-projects/samples/red_team/sample_red_team_async.py +++ b/sdk/ai/azure-ai-projects/samples/red_team/sample_red_team_async.py @@ -53,7 +53,6 @@ async def sample_red_team_async() -> None: DefaultAzureCredential() as credential, AIProjectClient(endpoint=endpoint, credential=credential) 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 @@ -85,7 +84,6 @@ async def sample_red_team_async() -> None: print("Listing all Red Team scans") async for scan in project_client.beta.red_teams.list(): print(f"Found scan: {scan.name}, Status: {scan.status}") - # [END red_team_sample] if __name__ == "__main__": diff --git a/sdk/ai/azure-ai-projects/tests/agents/telemetry/test_non_recording_span.py b/sdk/ai/azure-ai-projects/tests/agents/telemetry/test_non_recording_span.py new file mode 100644 index 000000000000..c64b0288bdb1 --- /dev/null +++ b/sdk/ai/azure-ai-projects/tests/agents/telemetry/test_non_recording_span.py @@ -0,0 +1,202 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +""" +Tests verifying that instrumentors correctly skip non-recording spans. + +When a span is not recording, the instrumentor must not attempt to write +attributes or events to it. These tests use a mock span whose +``is_recording()`` returns False and whose mutation methods raise +``AssertionError`` if called, ensuring the guards work correctly. +""" + +import os +import json +import pytest +from unittest.mock import MagicMock, PropertyMock + +from azure.ai.projects.telemetry._ai_project_instrumentor import ( + _AIAgentsInstrumentorPreview, +) +from azure.ai.projects.telemetry._responses_instrumentor import ( + _ResponsesInstrumentorPreview, +) + + +def _make_non_recording_span(): + """Return a mock AbstractSpan wrapping a non-recording OTel span. + + * ``span_instance.is_recording()`` returns ``False`` + * ``span_instance.is_recording`` (the property/method) also returns ``False`` + so the guard correctly skips writes. + * Any call to ``add_event``, ``set_status``, ``record_exception`` or + ``set_attribute`` raises ``AssertionError``, catching any code path + that fails to check ``is_recording()`` properly. + """ + span_instance = MagicMock() + span_instance.is_recording = MagicMock(return_value=False) + span_instance.add_event = MagicMock(side_effect=AssertionError("add_event called on non-recording span")) + span_instance.set_status = MagicMock(side_effect=AssertionError("set_status called on non-recording span")) + span_instance.record_exception = MagicMock( + side_effect=AssertionError("record_exception called on non-recording span") + ) + + span = MagicMock() + span.span_instance = span_instance + span.add_attribute = MagicMock(side_effect=AssertionError("add_attribute called on non-recording span")) + return span + + +class TestNonRecordingSpanProjectInstrumentor: + """Verify _AIAgentsInstrumentorPreview skips non-recording spans.""" + + def test_add_message_event_skips_non_recording_span(self): + """_add_message_event should not write to a non-recording span.""" + instrumentor = _AIAgentsInstrumentorPreview() + span = _make_non_recording_span() + + # This must not raise; the guard should return early. + instrumentor._add_message_event(span, role="user", content="hello") + + def test_add_instructions_event_skips_non_recording_span(self): + """_add_instructions_event should not write to a non-recording span.""" + instrumentor = _AIAgentsInstrumentorPreview() + span = _make_non_recording_span() + + instrumentor._add_instructions_event(span, instructions="Be helpful", additional_instructions=None) + + def test_start_create_agent_span_skips_non_recording_span(self): + """start_create_agent_span should not write attributes to a non-recording span.""" + instrumentor = _AIAgentsInstrumentorPreview() + + # We need to patch start_span to return our non-recording span + from unittest.mock import patch + + non_recording_span = _make_non_recording_span() + + with patch( + "azure.ai.projects.telemetry._ai_project_instrumentor.start_span", + return_value=non_recording_span, + ): + result = instrumentor.start_create_agent_span( + server_address="test.openai.azure.com", + port=443, + model="gpt-4", + name="test-agent", + instructions="Be helpful", + ) + + # Should return the span but not have written any attributes/events to it + assert result is non_recording_span + non_recording_span.add_attribute.assert_not_called() + non_recording_span.span_instance.add_event.assert_not_called() + + +class TestNonRecordingSpanResponsesInstrumentor: + """Verify _ResponsesInstrumentorPreview skips non-recording spans.""" + + def test_set_span_attribute_safe_skips_non_recording_span(self): + """_set_span_attribute_safe should not write to a non-recording span.""" + instrumentor = _ResponsesInstrumentorPreview() + span = _make_non_recording_span() + + # This must not raise; the guard should return early. + instrumentor._set_span_attribute_safe(span, "test.key", "test_value") + + def test_start_responses_span_skips_non_recording_span(self): + """start_responses_span should not write attributes to a non-recording span.""" + instrumentor = _ResponsesInstrumentorPreview() + + from unittest.mock import patch + + non_recording_span = _make_non_recording_span() + + with patch( + "azure.ai.projects.telemetry._responses_instrumentor.start_span", + return_value=non_recording_span, + ): + result = instrumentor.start_responses_span( + server_address="test.openai.azure.com", + port=443, + model="gpt-4", + assistant_name="test-agent", + conversation_id="conv-123", + input_text="Hello", + ) + + assert result is non_recording_span + non_recording_span.add_attribute.assert_not_called() + non_recording_span.span_instance.add_event.assert_not_called() + + def test_start_create_conversation_span_skips_non_recording_span(self): + """start_create_conversation_span should not write to a non-recording span.""" + instrumentor = _ResponsesInstrumentorPreview() + + from unittest.mock import patch + + non_recording_span = _make_non_recording_span() + + with patch( + "azure.ai.projects.telemetry._responses_instrumentor.start_span", + return_value=non_recording_span, + ): + result = instrumentor.start_create_conversation_span( + server_address="test.openai.azure.com", + port=443, + ) + + assert result is non_recording_span + non_recording_span.add_attribute.assert_not_called() + non_recording_span.span_instance.add_event.assert_not_called() + + def test_start_list_conversation_items_span_skips_non_recording_span(self): + """start_list_conversation_items_span should not write to a non-recording span.""" + instrumentor = _ResponsesInstrumentorPreview() + + from unittest.mock import patch + + non_recording_span = _make_non_recording_span() + + with patch( + "azure.ai.projects.telemetry._responses_instrumentor.start_span", + return_value=non_recording_span, + ): + result = instrumentor.start_list_conversation_items_span( + server_address="test.openai.azure.com", + port=443, + conversation_id="conv-123", + ) + + assert result is non_recording_span + non_recording_span.add_attribute.assert_not_called() + non_recording_span.span_instance.add_event.assert_not_called() + + def test_add_tool_call_events_skips_non_recording_span(self): + """_add_tool_call_events should not write to a non-recording span.""" + instrumentor = _ResponsesInstrumentorPreview() + span = _make_non_recording_span() + + # Create a mock response with function call output + mock_response = MagicMock() + mock_output_item = MagicMock() + mock_output_item.type = "function_call" + mock_output_item.name = "get_weather" + mock_output_item.call_id = "call_123" + mock_output_item.arguments = '{"city": "Seattle"}' + mock_response.output = [mock_output_item] + + instrumentor._add_tool_call_events(span, mock_response) + + def test_add_conversation_item_event_skips_non_recording_span(self): + """_add_conversation_item_event should not write to a non-recording span.""" + instrumentor = _ResponsesInstrumentorPreview() + span = _make_non_recording_span() + + mock_item = MagicMock() + mock_item.id = "item_123" + mock_item.type = "message" + mock_item.role = "user" + mock_item.content = [] + + instrumentor._add_conversation_item_event(span, mock_item) diff --git a/sdk/ai/azure-ai-projects/tests/agents/test_agent_create_version_exception.py b/sdk/ai/azure-ai-projects/tests/agents/test_agent_create_version_exception.py index d013c33e9847..076351f49a78 100644 --- a/sdk/ai/azure-ai-projects/tests/agents/test_agent_create_version_exception.py +++ b/sdk/ai/azure-ai-projects/tests/agents/test_agent_create_version_exception.py @@ -10,7 +10,7 @@ from devtools_testutils import recorded_by_proxy, RecordedTransport from azure.core.exceptions import HttpResponseError from azure.ai.projects.models import WorkflowAgentDefinition -from azure.ai.projects.operations._patch_agents import _PREVIEW_FEATURE_ADDED_ERROR_MESSAGE +from azure.ai.projects.models._patch import _PREVIEW_FEATURE_ADDED_ERROR_MESSAGE # Minimal workflow YAML — the service rejects the request before validating the # definition, so the content only needs to be a non-empty string. diff --git a/sdk/ai/azure-ai-projects/tests/agents/test_agent_create_version_exception_async.py b/sdk/ai/azure-ai-projects/tests/agents/test_agent_create_version_exception_async.py index a4ebac76ffa6..f60b7f38fa4c 100644 --- a/sdk/ai/azure-ai-projects/tests/agents/test_agent_create_version_exception_async.py +++ b/sdk/ai/azure-ai-projects/tests/agents/test_agent_create_version_exception_async.py @@ -11,7 +11,7 @@ from devtools_testutils import RecordedTransport from azure.core.exceptions import HttpResponseError from azure.ai.projects.models import WorkflowAgentDefinition -from azure.ai.projects.operations._patch_agents import _PREVIEW_FEATURE_ADDED_ERROR_MESSAGE +from azure.ai.projects.models._patch import _PREVIEW_FEATURE_ADDED_ERROR_MESSAGE # Minimal workflow YAML — the service rejects the request before validating the # definition, so the content only needs to be a non-empty string. diff --git a/sdk/ai/azure-ai-projects/tests/conftest.py b/sdk/ai/azure-ai-projects/tests/conftest.py index a6dd76036377..9ebb3ace9ec6 100644 --- a/sdk/ai/azure-ai-projects/tests/conftest.py +++ b/sdk/ai/azure-ai-projects/tests/conftest.py @@ -134,6 +134,82 @@ def sanitize_url_paths(): # Pattern 2: "Eval Run for -" (agent name already sanitized) add_general_regex_sanitizer(regex=r"sanitized-agent-name -\d{10}", value="sanitized-agent-name -SANITIZED-TS") + # Sanitize per-recording random model name used by `.beta.models` sample tests. + # Live re-recordings need a unique `/` namespace (Foundry's + # asset store reserves it permanently after `delete`), so we use a random + # suffix at recording time and normalize it here so playback URLs match. + add_general_regex_sanitizer(regex=r"recsmplmdl[a-f0-9]+", value="recsmplmdl00000000") + + # Sanitize Foundry project-managed Azure Storage account hostnames returned + # by `.beta.models.pending_upload` (shape: `sa<14 hex chars>.blob.core.windows.net`). + add_general_regex_sanitizer( + regex=r"sa[a-z0-9]{14,}\.blob\.core\.windows\.net", + value="sanitized-storage-account.blob.core.windows.net", + ) + + # Sanitize the per-pending-upload container name returned by Foundry + # (shape: `-pr-`). + add_general_regex_sanitizer( + regex=r"/[a-z0-9-]+-pr-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", + value="/sanitized-pending-upload-container", + ) + + # Sanitize SAS-token query strings returned by `.beta.models.pending_upload` + # (signed URLs to a Foundry-managed Storage container). Match conservatively + # on the `sig=` parameter which is unique to SAS tokens and isn't used by + # regular Azure API URLs. + add_general_regex_sanitizer( + regex=r"sig=[A-Za-z0-9%]+", + value="sig=sanitized-sas-sig", + ) + add_general_regex_sanitizer( + regex=r"skoid=[A-Fa-f0-9\-]+", + value="skoid=00000000-0000-0000-0000-000000000000", + ) + add_general_regex_sanitizer( + regex=r"sktid=[A-Fa-f0-9\-]+", + value="sktid=00000000-0000-0000-0000-000000000000", + ) + + # Sanitize `/workspaces/` URL segments (some Foundry asset-store URLs + # reference the underlying ML workspace by name, which leaks the project + # resource name). + add_general_regex_sanitizer( + regex=r"/workspaces/([-\w\._\(\)]+)", + value=sanitized_values["account_name"], + group_for_replace="1", + ) + + # Sanitize Foundry `azureai://` asset URIs whose `accounts/` and + # `projects/` segments embed the project resource name. + add_general_regex_sanitizer( + regex=r"azureai://accounts/([^/]+)/projects/([^/]+)", + value=f"azureai://accounts/{sanitized_values['account_name']}/projects/{sanitized_values['project_name']}", + ) + + # Sanitize the live Foundry project's account/project names anywhere they + # appear in URLs, headers, or bodies. Derived from the live endpoint shape + # `https://.services.ai.azure.com/api/projects/` so we + # cover trailing leaks like `@@AML/...` asset IDs and + # `publisherId` fields that aren't matched by URL-segment sanitizers. + _live_endpoint = os.environ.get("FOUNDRY_PROJECT_ENDPOINT") or os.environ.get("foundry_project_endpoint") + if _live_endpoint: + _ep_match = re.match( + r"https?://(?P[^.]+)\.[^/]+/api/projects/(?P[^/?#]+)", + _live_endpoint, + ) + if _ep_match: + _live_account = _ep_match.group("account") + _live_project = _ep_match.group("project") + # Order matters: the longer (account) name often contains the shorter + # (project) name as a prefix; replace the longer one first. + for _name, _placeholder in ( + (_live_account, sanitized_values["account_name"]), + (_live_project, sanitized_values["project_name"]), + ): + add_general_regex_sanitizer(regex=re.escape(_name), value=_placeholder) + add_body_string_sanitizer(target=_name, value=_placeholder) + # Sanitize image-generation deployment name from live env when present. # This value is commonly emitted in request headers (for example # `x-ms-oai-image-generation-deployment`) and may come from either @@ -181,6 +257,24 @@ def sanitize_url_paths(): regex=r"(?i)^Bearer\s+github_pat_[A-Za-z0-9_]+$", ) + # Sanitize raw Entra-ID JWTs (no "Bearer " prefix) passed via MCPTool.authorization + # to match the `fake_token` value the FakeTokenCredential returns during playback. + add_body_key_sanitizer( + json_path="$..authorization", + value="fake_token", + regex=r"^eyJ[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+$", + ) + + # Sanitize Cognitive Services / Foundry account hostnames inside request and + # response bodies (e.g. MCPTool.server_url built from FOUNDRY_PROJECT_ENDPOINT). + # URL-path sanitizers above already redact /accounts/, /projects/, etc., + # but the host is built into body fields and needs its own redaction so + # recordings match the playback FOUNDRY_PROJECT_ENDPOINT. + add_body_regex_sanitizer( + regex=r"https://[a-z0-9-]+\.services\.ai\.azure\.com", + value=f"https://{SanitizedValues.ACCOUNT_NAME}.services.ai.azure.com", + ) + # Sanitize Azure Blob account host while preserving container path and SAS shape. # This avoids creating inconsistent recordings where sasUri points to a different # container than the corresponding blob RequestUri entries. @@ -213,6 +307,11 @@ def sanitize_url_paths(): headers="x-stainless-arch, x-stainless-async, x-stainless-lang, x-stainless-os, x-stainless-package-version, x-stainless-read-timeout, x-stainless-retry-count, x-stainless-runtime, x-stainless-runtime-version" ) + # Strip Content-Encoding so playback doesn't try to decompress a body that the test-proxy + # has already stored decoded (notably brotli responses from openai endpoints which httpx + # would otherwise fail to decode -> UnicodeDecodeError). + add_remove_header_sanitizer(headers="Content-Encoding") + # Remove the following sanitizers since certain fields are needed in tests and are non-sensitive: # - AZSDK3493: $..name # - AZSDK3430: $..id 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 2c80a40f9902..70c218bc1e47 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 @@ -39,11 +39,37 @@ "evaluators": "Evaluations=V1Preview", "insights": "Insights=V1Preview", "memory_stores": "MemoryStores=V1Preview", + "models": "Models=V1Preview", "red_teams": "RedTeams=V1Preview", + "routines": "Routines=V1Preview", "schedules": "Schedules=V1Preview", "toolboxes": "Toolboxes=V1Preview", "skills": "Skills=V1Preview", - "agents": "HostedAgents=V1Preview,AgentEndpoints=V1Preview", + "datasets": "DataGenerationJobs=V1Preview", + "agents": "HostedAgents=V1Preview,WorkflowAgents=V1Preview,AgentEndpoints=V1Preview,CodeAgents=V1Preview,ExternalAgents=V1Preview,AgentsOptimization=V1Preview", +} + +# Methods on .beta sub-clients that are NOT simple one-HTTP-call wrappers and +# therefore cannot be exercised by the generic header-injection test (which +# captures the first outgoing HttpRequest). +# +# Multi-step orchestrator helpers (validate locally -> HTTP -> external process +# -> HTTP -> poll) fall into this bucket: they perform local input validation +# and/or external side effects (e.g. subprocess calls) before the first HTTP +# request, so synthetic placeholder arguments cause them to abort with a +# TypeError/ValueError/RuntimeError before any request is ever sent. +# +# The header-injection invariant is still enforced for these methods because +# every nested HTTP call they make is routed through other public sub-client +# methods that ARE covered by this test (e.g. .beta.models.create internally +# calls .beta.models.pending_upload and .beta.models.pending_create_version, +# both of which are tested separately and pass). +# +# Format: { "": frozenset({"", ...}) } +EXCLUDED_BETA_METHODS: dict[str, frozenset] = { + "models": frozenset( + {"create"} + ), # multi-step helper: validate -> pending_upload -> azcopy -> pending_create_version -> poll get } # Shared test cases for non-beta methods that optionally send the Foundry-Features header. @@ -58,7 +84,7 @@ # The test id is derived automatically from method_name. pytest.param( "agents.create_version", - "HostedAgents=V1Preview,WorkflowAgents=V1Preview,AgentEndpoints=V1Preview", + "HostedAgents=V1Preview,WorkflowAgents=V1Preview,AgentEndpoints=V1Preview,CodeAgents=V1Preview,ExternalAgents=V1Preview,AgentsOptimization=V1Preview", ), pytest.param( "evaluation_rules.create_or_update", diff --git a/sdk/ai/azure-ai-projects/tests/foundry_features_header/test_foundry_features_header_on_beta_operations.py b/sdk/ai/azure-ai-projects/tests/foundry_features_header/test_foundry_features_header_on_beta_operations.py index ac3186a1225f..43db5c0811f6 100644 --- a/sdk/ai/azure-ai-projects/tests/foundry_features_header/test_foundry_features_header_on_beta_operations.py +++ b/sdk/ai/azure-ai-projects/tests/foundry_features_header/test_foundry_features_header_on_beta_operations.py @@ -38,6 +38,7 @@ from azure.ai.projects import AIProjectClient from foundry_features_header_test_base import ( + EXCLUDED_BETA_METHODS, EXPECTED_FOUNDRY_FEATURES, FAKE_ENDPOINT, FOUNDRY_FEATURES_HEADER, @@ -111,9 +112,12 @@ def _discover_test_cases() -> list[pytest.param]: # return no public methods. Methods are still fetched via getattr(sc, ...) so # the header-injecting proxy wrapper is exercised. _underlying_op = getattr(sc, "_operation", sc) + _excluded = EXCLUDED_BETA_METHODS.get(sc_name, frozenset()) for m_name in sorted(dir(_underlying_op)): if m_name.startswith("_"): continue + if m_name in _excluded: + continue method = getattr(sc, m_name) if not callable(method): continue diff --git a/sdk/ai/azure-ai-projects/tests/foundry_features_header/test_foundry_features_header_on_beta_operations_async.py b/sdk/ai/azure-ai-projects/tests/foundry_features_header/test_foundry_features_header_on_beta_operations_async.py index 30eed85005a5..afb065d6a155 100644 --- a/sdk/ai/azure-ai-projects/tests/foundry_features_header/test_foundry_features_header_on_beta_operations_async.py +++ b/sdk/ai/azure-ai-projects/tests/foundry_features_header/test_foundry_features_header_on_beta_operations_async.py @@ -40,6 +40,7 @@ from azure.ai.projects.aio import AIProjectClient as AsyncAIProjectClient from foundry_features_header_test_base import ( + EXCLUDED_BETA_METHODS, EXPECTED_FOUNDRY_FEATURES, FAKE_ENDPOINT, FOUNDRY_FEATURES_HEADER, @@ -116,9 +117,12 @@ def _discover_async_test_cases() -> list[pytest.param]: # return no public methods. Methods are still fetched via getattr(sc, ...) so # the header-injecting proxy wrapper is exercised. _underlying_op = getattr(sc, "_operation", sc) + _excluded = EXCLUDED_BETA_METHODS.get(sc_name, frozenset()) for m_name in sorted(dir(_underlying_op)): if m_name.startswith("_"): continue + if m_name in _excluded: + continue method = getattr(sc, m_name) if not callable(method): continue diff --git a/sdk/ai/azure-ai-projects/tests/models/test_models.py b/sdk/ai/azure-ai-projects/tests/models/test_models.py new file mode 100644 index 000000000000..356a83d14d3e --- /dev/null +++ b/sdk/ai/azure-ai-projects/tests/models/test_models.py @@ -0,0 +1,141 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Live, recorded tests for ``project_client.beta.models``. + +These tests exercise the generated ``BetaModelsOperations`` (``list``, +``list_versions``, ``get``, ``pending_upload``, ``get_credentials``, ``delete``) +and the patched ``create`` end-to-end helper. They follow the same +"upload + record" pattern used by ``test_datasets.py``. + +``create_or_update`` is intentionally not tested here. The Foundry data plane +currently returns ``404 Not Found`` for that route even when ``GET`` for the +same name/version succeeds. The cell will be re-enabled once the service-side +issue is fixed. +""" + +import os + +import pytest +from devtools_testutils import ( + add_general_regex_sanitizer, + is_live, + is_live_and_not_recording, + recorded_by_proxy, +) +from azure.core.exceptions import HttpResponseError, ResourceNotFoundError + +from test_base import TestBase, servicePreparer + +# Construct the path to the data folder used in this test +script_dir = os.path.dirname(os.path.abspath(__file__)) +data_folder = os.environ.get("DATA_FOLDER", os.path.join(script_dir, "../test_data/models")) + + +@pytest.mark.skipif( + not is_live_and_not_recording(), + reason="Skipped when using recordings due to flakiness of recording blob storage calls", +) +class TestModels(TestBase): + + # cls & pytest tests\models\test_models.py::TestModels::test_create -s + @servicePreparer() + @recorded_by_proxy + def test_create(self, **kwargs): + """End-to-end: pending_upload -> azcopy -> pending_create_version -> get/list/delete.""" + model_name = self.test_models_params["model_name_1"] + model_version = self.test_models_params["model_version"] + expected_model_name = model_name if is_live() else "sanitized-model-name" + add_general_regex_sanitizer(regex=r"test-model-name-\d{5}", value="sanitized-model-name", function_scoped=True) + + with self.create_client(**kwargs) as project_client: + + print(f"[test_create] create {model_name}@{model_version}") + registered = project_client.beta.models.create( + name=model_name, + version=model_version, + source=data_folder, + weight_type="FullWeight", + description="Registered by test_create", + tags={"source": "test_models.py"}, + ) + assert registered is not None + assert registered.name == expected_model_name + assert registered.version == model_version + assert registered.blob_uri, "blob_uri should be populated after create" + + print(f"[test_create] get {model_name}@{model_version}") + fetched = project_client.beta.models.get(name=model_name, version=model_version) + assert fetched.id == registered.id + assert fetched.name == expected_model_name + assert fetched.version == model_version + + print(f"[test_create] list_versions({model_name!r})") + versions = list(project_client.beta.models.list_versions(name=model_name)) + assert any( + mv.version == model_version for mv in versions + ), f"version {model_version!r} not found in list_versions" + + print("[test_create] list (latest of every model)") + empty = True + for mv in project_client.beta.models.list(): + empty = False + assert mv.name and mv.version + assert not empty, "list() returned no models even though we just registered one" + + print(f"[test_create] get_credentials {model_name}@{model_version}") + from azure.ai.projects.models import ModelCredentialRequest + + creds = project_client.beta.models.get_credentials( + name=model_name, + version=model_version, + body=ModelCredentialRequest(blob_uri=registered.blob_uri), + ) + blob_ref = getattr(creds, "blob_reference_for_consumption", None) or getattr(creds, "blob_reference", None) + assert blob_ref is not None, f"no blob reference in credentials response: {creds!r}" + assert blob_ref.blob_uri + assert blob_ref.credential is not None + assert blob_ref.credential.sas_uri + + print(f"[test_create] delete {model_name}@{model_version}") + try: + project_client.beta.models.delete(name=model_name, version=model_version) + except HttpResponseError as ex: + # The service currently returns 200 OK for a successful DELETE while + # the generated operation only allow-lists 204. Tolerate that here. + if ex.status_code != 200: + raise + + print(f"[test_create] get on deleted {model_name}@{model_version} should 404") + with pytest.raises((ResourceNotFoundError, HttpResponseError)): + project_client.beta.models.get(name=model_name, version=model_version) + + # cls & pytest tests\models\test_models.py::TestModels::test_models_pending_upload -s + @servicePreparer() + @recorded_by_proxy + def test_models_pending_upload(self, **kwargs): + """Lower-level: ``pending_upload`` returns a usable SAS URI.""" + from azure.ai.projects.models import ModelPendingUploadRequest, PendingUploadType + + model_name = self.test_models_params["model_name_2"] + model_version = self.test_models_params["model_version"] + add_general_regex_sanitizer(regex=r"test-model-name-\d{5}", value="sanitized-model-name", function_scoped=True) + + with self.create_client(**kwargs) as project_client: + + print(f"[test_models_pending_upload] pending_upload {model_name}@{model_version}") + pending = project_client.beta.models.pending_upload( + name=model_name, + version=model_version, + body=ModelPendingUploadRequest( + pending_upload_type=PendingUploadType.TEMPORARY_BLOB_REFERENCE, + ), + ) + payload = pending.as_dict() if hasattr(pending, "as_dict") else dict(pending) + blob_ref = payload.get("blobReferenceForConsumption") or payload.get("blobReference") or {} + assert (blob_ref.get("credential") or {}).get( + "sasUri" + ), f"pending_upload response missing SAS URI: {payload!r}" + assert blob_ref.get("blobUri"), f"pending_upload response missing blobUri: {payload!r}" diff --git a/sdk/ai/azure-ai-projects/tests/models/test_models_async.py b/sdk/ai/azure-ai-projects/tests/models/test_models_async.py new file mode 100644 index 000000000000..b92c0c6e54c1 --- /dev/null +++ b/sdk/ai/azure-ai-projects/tests/models/test_models_async.py @@ -0,0 +1,98 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Live, recorded async tests for ``project_client.beta.models``. + +Mirrors :mod:`tests.models.test_models` for the async client. ``create`` +itself is implemented only on the sync client (it shells out to ``azcopy``); the +async surface is exercised via ``list``, ``list_versions``, ``get`` and +``delete`` against a model registered with the sync helper as part of the +fixture. +""" + +import os + +import pytest +from devtools_testutils import ( + add_general_regex_sanitizer, + is_live, + is_live_and_not_recording, +) +from devtools_testutils.aio import recorded_by_proxy_async +from azure.core.exceptions import HttpResponseError, ResourceNotFoundError + +from test_base import TestBase, servicePreparer + +script_dir = os.path.dirname(os.path.abspath(__file__)) +data_folder = os.environ.get("DATA_FOLDER", os.path.join(script_dir, "../test_data/models")) + + +@pytest.mark.skipif( + not is_live_and_not_recording(), + reason="Skipped when using recordings due to flakiness of recording blob storage calls", +) +class TestModelsAsync(TestBase): + + # cls & pytest tests\models\test_models_async.py::TestModelsAsync::test_models_async_list_get_delete -s + @servicePreparer() + @recorded_by_proxy_async + async def test_models_async_list_get_delete(self, **kwargs): + """Register a model with the sync helper, then drive list/get/delete async.""" + from azure.ai.projects import AIProjectClient as SyncAIProjectClient + + model_name = self.test_models_params["model_name_1"] + model_version = self.test_models_params["model_version"] + expected_model_name = model_name if is_live() else "sanitized-model-name" + add_general_regex_sanitizer(regex=r"test-model-name-\d{5}", value="sanitized-model-name", function_scoped=True) + + endpoint = kwargs["foundry_project_endpoint"] + + # Set up: register a model using the sync helper (azcopy is sync). + with SyncAIProjectClient( + endpoint=endpoint, + credential=self.get_credential(SyncAIProjectClient, is_async=False), + ) as sync_client: + registered = sync_client.beta.models.create( + name=model_name, + version=model_version, + source=data_folder, + weight_type="FullWeight", + description="Registered by test_models_async", + tags={"source": "test_models_async.py"}, + ) + assert registered is not None + assert registered.name == expected_model_name + + # Exercise: drive the async client. + async with self.create_async_client(**kwargs) as project_client: + + print(f"[test_models_async] get {model_name}@{model_version}") + fetched = await project_client.beta.models.get(name=model_name, version=model_version) + assert fetched.name == expected_model_name + assert fetched.version == model_version + + print(f"[test_models_async] list_versions({model_name!r})") + versions = [] + async for mv in project_client.beta.models.list_versions(name=model_name): + versions.append(mv) + assert any(mv.version == model_version for mv in versions) + + print("[test_models_async] list (latest of every model)") + seen = 0 + async for mv in project_client.beta.models.list(): + seen += 1 + assert mv.name and mv.version + assert seen > 0 + + print(f"[test_models_async] delete {model_name}@{model_version}") + try: + await project_client.beta.models.delete(name=model_name, version=model_version) + except HttpResponseError as ex: + if ex.status_code != 200: + raise + + print(f"[test_models_async] get on deleted {model_name}@{model_version} should 404") + with pytest.raises((ResourceNotFoundError, HttpResponseError)): + await project_client.beta.models.get(name=model_name, version=model_version) diff --git a/sdk/ai/azure-ai-projects/tests/models/test_patch_models.py b/sdk/ai/azure-ai-projects/tests/models/test_patch_models.py new file mode 100644 index 000000000000..0d2811e9492c --- /dev/null +++ b/sdk/ai/azure-ai-projects/tests/models/test_patch_models.py @@ -0,0 +1,436 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Offline unit tests for ``azure.ai.projects.operations._patch_models``. + +These tests do not contact the Foundry service. They cover the patch helpers +``_extract_pending_upload_targets`` and ``_run_azcopy``, and the orchestration +performed by ``create`` (mocking ``pending_upload``, ``pending_create_version`` +and ``get`` on the base class). +""" + +from __future__ import annotations + +import os +import subprocess +from pathlib import Path +from types import SimpleNamespace +from unittest import mock + +import pytest + +from azure.core.exceptions import ResourceNotFoundError + +from azure.ai.projects.operations._patch_models import BetaModelsOperations + +# --------------------------------------------------------------------------- +# _extract_pending_upload_targets +# --------------------------------------------------------------------------- + + +class TestExtractPendingUploadTargets: + def test_datastore_wire_shape(self): + """The service currently returns ``blobReferenceForConsumption``.""" + payload = { + "blobReferenceForConsumption": { + "blobUri": "https://acct.blob.core.windows.net/c/path", + "credential": {"sasUri": "https://acct.blob.core.windows.net/c/path?sig=abc"}, + }, + "temporaryDataReferenceId": "abc-123", + } + sas, blob, pid = BetaModelsOperations._extract_pending_upload_targets(payload) + assert sas == payload["blobReferenceForConsumption"]["credential"]["sasUri"] + assert blob == payload["blobReferenceForConsumption"]["blobUri"] + assert pid == "abc-123" + + def test_modeled_wire_shape(self): + """The SDK-modeled shape uses ``blobReference`` / ``pendingUploadId``.""" + payload = { + "blobReference": { + "blobUri": "https://acct.blob.core.windows.net/c/path", + "credential": {"sasUri": "https://acct.blob.core.windows.net/c/path?sig=xyz"}, + }, + "pendingUploadId": "modeled-id", + } + sas, blob, pid = BetaModelsOperations._extract_pending_upload_targets(payload) + assert sas == payload["blobReference"]["credential"]["sasUri"] + assert blob == payload["blobReference"]["blobUri"] + assert pid == "modeled-id" + + def test_response_object_with_as_dict(self): + """Accept anything exposing ``.as_dict()`` (the modeled response type).""" + payload = { + "blobReference": { + "blobUri": "https://x/y", + "credential": {"sasUri": "https://x/y?sas"}, + }, + "pendingUploadId": "id1", + } + response = SimpleNamespace(as_dict=lambda: payload) + sas, blob, pid = BetaModelsOperations._extract_pending_upload_targets(response) + assert (sas, blob, pid) == ("https://x/y?sas", "https://x/y", "id1") + + def test_missing_sas_uri_raises(self): + payload = { + "blobReferenceForConsumption": {"blobUri": "https://x/y", "credential": {}}, + } + with pytest.raises(ValueError, match="SAS URI / blob URI"): + BetaModelsOperations._extract_pending_upload_targets(payload) + + def test_missing_blob_uri_raises(self): + payload = { + "blobReferenceForConsumption": {"credential": {"sasUri": "https://x?sas"}}, + } + with pytest.raises(ValueError, match="SAS URI / blob URI"): + BetaModelsOperations._extract_pending_upload_targets(payload) + + def test_missing_pending_upload_id_is_none(self): + payload = { + "blobReferenceForConsumption": { + "blobUri": "https://x/y", + "credential": {"sasUri": "https://x/y?sas"}, + }, + } + sas, blob, pid = BetaModelsOperations._extract_pending_upload_targets(payload) + assert pid is None + assert (sas, blob) == ("https://x/y?sas", "https://x/y") + + +# --------------------------------------------------------------------------- +# _run_azcopy +# --------------------------------------------------------------------------- + + +class TestRunAzcopy: + def test_missing_azcopy_raises_runtime_error(self, tmp_path): + src = tmp_path / "weights.bin" + src.write_bytes(b"x") + with mock.patch("azure.ai.projects.operations._patch_models.shutil.which", return_value=None): + with pytest.raises(RuntimeError, match="azcopy"): + BetaModelsOperations._run_azcopy(src, "https://x?sas") + + def test_explicit_path_overrides_shutil_which(self, tmp_path): + src = tmp_path / "weights.bin" + src.write_bytes(b"x") + completed = SimpleNamespace(returncode=0, stdout="", stderr="") + with mock.patch( + "azure.ai.projects.operations._patch_models.subprocess.run", + return_value=completed, + ) as run, mock.patch("azure.ai.projects.operations._patch_models.shutil.which", return_value=None): + BetaModelsOperations._run_azcopy(src, "https://x?sas", azcopy_path="/opt/azcopy") + assert run.call_args.args[0][0] == "/opt/azcopy" + + def test_directory_source_uses_glob_arg(self, tmp_path): + (tmp_path / "weights.bin").write_bytes(b"x") + completed = SimpleNamespace(returncode=0, stdout="", stderr="") + with mock.patch( + "azure.ai.projects.operations._patch_models.subprocess.run", + return_value=completed, + ) as run, mock.patch("azure.ai.projects.operations._patch_models.shutil.which", return_value="/usr/bin/azcopy"): + BetaModelsOperations._run_azcopy(tmp_path, "https://x?sas") + cmd = run.call_args.args[0] + assert cmd[0] == "/usr/bin/azcopy" + assert cmd[1] == "copy" + assert cmd[2] == str(tmp_path / "*") + assert cmd[3] == "https://x?sas" + assert "--from-to" in cmd + assert cmd[cmd.index("--from-to") + 1] == "LocalBlob" + assert "--recursive" in cmd + + def test_file_source_uses_file_arg(self, tmp_path): + src = tmp_path / "weights.bin" + src.write_bytes(b"x") + completed = SimpleNamespace(returncode=0, stdout="", stderr="") + with mock.patch( + "azure.ai.projects.operations._patch_models.subprocess.run", + return_value=completed, + ) as run, mock.patch("azure.ai.projects.operations._patch_models.shutil.which", return_value="/usr/bin/azcopy"): + BetaModelsOperations._run_azcopy(src, "https://x?sas") + assert run.call_args.args[0][2] == str(src) + + def test_missing_source_raises_value_error(self, tmp_path): + ghost = tmp_path / "does-not-exist" + with mock.patch("azure.ai.projects.operations._patch_models.shutil.which", return_value="/usr/bin/azcopy"): + with pytest.raises(ValueError, match="does not exist"): + BetaModelsOperations._run_azcopy(ghost, "https://x?sas") + + def test_nonzero_exit_raises_runtime_error(self, tmp_path): + src = tmp_path / "weights.bin" + src.write_bytes(b"x") + completed = SimpleNamespace(returncode=1, stdout="oops-stdout", stderr="oops-stderr") + with mock.patch( + "azure.ai.projects.operations._patch_models.subprocess.run", + return_value=completed, + ), mock.patch("azure.ai.projects.operations._patch_models.shutil.which", return_value="/usr/bin/azcopy"): + with pytest.raises(RuntimeError, match="exited with code 1"): + BetaModelsOperations._run_azcopy(src, "https://x?sas") + + +# --------------------------------------------------------------------------- +# create orchestration +# --------------------------------------------------------------------------- + + +def _make_ops() -> BetaModelsOperations: + """Build a ``BetaModelsOperations`` without going through the client wiring.""" + return BetaModelsOperations.__new__(BetaModelsOperations) + + +def _pending_payload() -> dict: + return { + "blobReferenceForConsumption": { + "blobUri": "https://acct.blob.core.windows.net/c/path", + "credential": {"sasUri": "https://acct.blob.core.windows.net/c/path?sig=abc"}, + }, + "temporaryDataReferenceId": "pending-id-1", + } + + +class TestCreateVersionOrchestration: + @pytest.fixture(autouse=True) + def _stub_azcopy_on_path(self): + """Pretend azcopy is installed so the up-front validator passes.""" + with mock.patch( + "azure.ai.projects.operations._patch_models.shutil.which", + return_value="/usr/bin/azcopy", + ): + yield + + def test_models_create_runs_three_steps_in_order_and_returns_get_result(self, tmp_path): + (tmp_path / "weights.bin").write_bytes(b"x") + ops = _make_ops() + + committed = SimpleNamespace( + id="model/abc", + name="my-model", + version="1", + blob_uri="https://acct.blob.core.windows.net/c/path", + ) + calls: list[str] = [] + + def fake_pending_upload(**kwargs): + calls.append("pending_upload") + assert kwargs["name"] == "my-model" + assert kwargs["version"] == "1" + return _pending_payload() + + def fake_run_azcopy(source, sas_uri, *, azcopy_path=None): # noqa: ARG001 + calls.append("azcopy") + assert Path(source) == tmp_path + assert sas_uri.startswith("https://") + assert azcopy_path == "/custom/azcopy" + + def fake_create_async(**kwargs): + calls.append("pending_create_version") + assert kwargs["name"] == "my-model" + assert kwargs["version"] == "1" + body = kwargs["model_version"] + # The blob_uri from the pending response is plumbed into the commit body. + assert body.blob_uri == "https://acct.blob.core.windows.net/c/path" + assert body.weight_type == "FullWeight" + assert body.description == "desc" + assert body.tags == {"k": "v"} + + def fake_get(**kwargs): + calls.append("get") + assert kwargs["name"] == "my-model" + assert kwargs["version"] == "1" + return committed + + with mock.patch.object(ops, "pending_upload", side_effect=fake_pending_upload), mock.patch.object( + BetaModelsOperations, "_run_azcopy", staticmethod(fake_run_azcopy) + ), mock.patch.object(ops, "pending_create_version", side_effect=fake_create_async), mock.patch.object( + ops, "get", side_effect=fake_get + ): + result = ops.create( + name="my-model", + version="1", + source=tmp_path, + weight_type="FullWeight", + description="desc", + tags={"k": "v"}, + azcopy_path="/custom/azcopy", + ) + + assert result is committed + assert calls == ["pending_upload", "azcopy", "pending_create_version", "get"] + + def test_models_create_wait_for_commit_false_returns_none_and_does_not_poll(self, tmp_path): + (tmp_path / "weights.bin").write_bytes(b"x") + ops = _make_ops() + get_mock = mock.Mock() + + with mock.patch.object(ops, "pending_upload", return_value=_pending_payload()), mock.patch.object( + BetaModelsOperations, "_run_azcopy", staticmethod(lambda *a, **kw: None) + ), mock.patch.object(ops, "pending_create_version", return_value=None), mock.patch.object(ops, "get", get_mock): + result = ops.create( + name="m", + version="1", + source=tmp_path, + wait_for_commit=False, + ) + + assert result is None + get_mock.assert_not_called() + + def test_models_create_polls_until_get_succeeds(self, tmp_path): + (tmp_path / "weights.bin").write_bytes(b"x") + ops = _make_ops() + committed = SimpleNamespace(name="m", version="1") + + get_mock = mock.Mock( + side_effect=[ + ResourceNotFoundError(message="not yet"), + ResourceNotFoundError(message="still not yet"), + committed, + ] + ) + + with mock.patch.object(ops, "pending_upload", return_value=_pending_payload()), mock.patch.object( + BetaModelsOperations, "_run_azcopy", staticmethod(lambda *a, **kw: None) + ), mock.patch.object(ops, "pending_create_version", return_value=None), mock.patch.object( + ops, "get", get_mock + ), mock.patch( + "azure.ai.projects.operations._patch_models.time.sleep" + ) as sleep: + result = ops.create( + name="m", + version="1", + source=tmp_path, + polling_interval=0.01, + ) + + assert result is committed + assert get_mock.call_count == 3 + assert sleep.call_count == 2 + + def test_models_create_polling_timeout_raises_runtime_error(self, tmp_path): + (tmp_path / "weights.bin").write_bytes(b"x") + ops = _make_ops() + + # First a real time.monotonic call (start), then an over-the-deadline one. + times = iter([1000.0, 1000.0, 9999.0]) + with mock.patch.object(ops, "pending_upload", return_value=_pending_payload()), mock.patch.object( + BetaModelsOperations, "_run_azcopy", staticmethod(lambda *a, **kw: None) + ), mock.patch.object(ops, "pending_create_version", return_value=None), mock.patch.object( + ops, "get", side_effect=ResourceNotFoundError(message="never") + ), mock.patch( + "azure.ai.projects.operations._patch_models.time.monotonic", + side_effect=lambda: next(times), + ), mock.patch( + "azure.ai.projects.operations._patch_models.time.sleep" + ): + with pytest.raises(RuntimeError, match="did not appear within"): + ops.create( + name="m", + version="1", + source=tmp_path, + polling_timeout=1.0, + ) + + def test_models_create_missing_source_raises_before_calling_service(self, tmp_path): + ops = _make_ops() + ghost = tmp_path / "does-not-exist" + pending = mock.Mock() + with mock.patch.object(ops, "pending_upload", pending): + with pytest.raises(ValueError, match="does not exist"): + ops.create(name="m", version="1", source=ghost) + pending.assert_not_called() + + +# --------------------------------------------------------------------------- +# _validate_create_inputs +# --------------------------------------------------------------------------- + + +class TestValidateCreateVersionInputs: + @pytest.fixture(autouse=True) + def _stub_azcopy_on_path(self): + with mock.patch( + "azure.ai.projects.operations._patch_models.shutil.which", + return_value="/usr/bin/azcopy", + ): + yield + + def _kwargs(self, **overrides): + defaults = dict( + name="m", + version="1", + source="/tmp/x", + azcopy_path=None, + wait_for_commit=True, + polling_timeout=300.0, + polling_interval=2.0, + ) + defaults.update(overrides) + return defaults + + def test_valid_directory_source_returns_path(self, tmp_path): + (tmp_path / "weights.bin").write_bytes(b"x") + result = BetaModelsOperations._validate_create_inputs(**self._kwargs(source=tmp_path)) + assert result == tmp_path + + @pytest.mark.parametrize("bad_name", ["", " ", None, 123]) + def test_empty_or_non_string_name_raises(self, tmp_path, bad_name): + (tmp_path / "weights.bin").write_bytes(b"x") + with pytest.raises(ValueError, match="`name`"): + BetaModelsOperations._validate_create_inputs(**self._kwargs(name=bad_name, source=tmp_path)) + + @pytest.mark.parametrize("bad_version", ["", " ", None, 1]) + def test_empty_or_non_string_version_raises(self, tmp_path, bad_version): + (tmp_path / "weights.bin").write_bytes(b"x") + with pytest.raises(ValueError, match="`version`"): + BetaModelsOperations._validate_create_inputs(**self._kwargs(version=bad_version, source=tmp_path)) + + def test_empty_directory_raises(self, tmp_path): + with pytest.raises(ValueError, match="directory is empty"): + BetaModelsOperations._validate_create_inputs(**self._kwargs(source=tmp_path)) + + def test_empty_file_raises(self, tmp_path): + empty = tmp_path / "weights.bin" + empty.write_bytes(b"") + with pytest.raises(ValueError, match="file is empty"): + BetaModelsOperations._validate_create_inputs(**self._kwargs(source=empty)) + + @pytest.mark.parametrize("bad_timeout", [0, -1.0]) + def test_non_positive_polling_timeout_raises(self, tmp_path, bad_timeout): + (tmp_path / "weights.bin").write_bytes(b"x") + with pytest.raises(ValueError, match="polling_timeout"): + BetaModelsOperations._validate_create_inputs(**self._kwargs(source=tmp_path, polling_timeout=bad_timeout)) + + @pytest.mark.parametrize("bad_interval", [0, -1.0]) + def test_non_positive_polling_interval_raises(self, tmp_path, bad_interval): + (tmp_path / "weights.bin").write_bytes(b"x") + with pytest.raises(ValueError, match="polling_interval"): + BetaModelsOperations._validate_create_inputs(**self._kwargs(source=tmp_path, polling_interval=bad_interval)) + + def test_polling_params_skipped_when_wait_for_commit_false(self, tmp_path): + (tmp_path / "weights.bin").write_bytes(b"x") + # Negative polling values are tolerated when not waiting for commit. + result = BetaModelsOperations._validate_create_inputs( + **self._kwargs(source=tmp_path, wait_for_commit=False, polling_timeout=-1, polling_interval=-1) + ) + assert result == tmp_path + + def test_missing_azcopy_raises(self, tmp_path): + (tmp_path / "weights.bin").write_bytes(b"x") + with mock.patch( + "azure.ai.projects.operations._patch_models.shutil.which", + return_value=None, + ): + with pytest.raises(RuntimeError, match="azcopy"): + BetaModelsOperations._validate_create_inputs(**self._kwargs(source=tmp_path)) + + def test_models_create_validates_before_calling_pending_upload(self, tmp_path): + """Validation runs before any service operation.""" + (tmp_path / "weights.bin").write_bytes(b"x") + ops = _make_ops() + pending = mock.Mock() + with mock.patch.object(ops, "pending_upload", pending), mock.patch( + "azure.ai.projects.operations._patch_models.shutil.which", return_value=None + ): + with pytest.raises(RuntimeError, match="azcopy"): + ops.create(name="m", version="1", source=tmp_path) + pending.assert_not_called() diff --git a/sdk/ai/azure-ai-projects/tests/responses/openai_test_helpers.py b/sdk/ai/azure-ai-projects/tests/responses/openai_test_helpers.py new file mode 100644 index 000000000000..fbf3637291ea --- /dev/null +++ b/sdk/ai/azure-ai-projects/tests/responses/openai_test_helpers.py @@ -0,0 +1,69 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Shared helpers for unit-testing AIProjectClient.get_openai_client (sync and async). + +These helpers build lightweight client stubs that bypass the real ``__init__`` so unit +tests can target individual branches of ``get_openai_client`` without making any +network calls. +""" + +from typing import Optional +from unittest.mock import MagicMock + +from azure.ai.projects import AIProjectClient +from azure.ai.projects.aio import AIProjectClient as AsyncAIProjectClient + +ENDPOINT = "https://myaccount.services.ai.azure.com/api/projects/myproject" +API_VERSION = "2025-01-01" + +# Patch targets used by tests to swap in mocked OpenAI/AsyncOpenAI constructors +# and bearer-token providers. +SYNC_OPENAI_PATCH = "azure.ai.projects._patch.OpenAI" +ASYNC_OPENAI_PATCH = "azure.ai.projects.aio._patch.AsyncOpenAI" +SYNC_TOKEN_PROVIDER_PATCH = "azure.ai.projects._patch.get_bearer_token_provider" +ASYNC_TOKEN_PROVIDER_PATCH = "azure.ai.projects.aio._patch.get_bearer_token_provider" + + +def make_sync_client( + allow_preview: bool = True, + console_logging: bool = False, + custom_user_agent: Optional[str] = None, +) -> AIProjectClient: + """Return a minimal sync AIProjectClient stub suitable for unit-testing get_openai_client.""" + client = AIProjectClient.__new__(AIProjectClient) + client._config = MagicMock() + client._config.endpoint = ENDPOINT + client._config.allow_preview = allow_preview + client._config.api_version = API_VERSION + client._config.credential = MagicMock() + client._console_logging_enabled = console_logging + client._custom_user_agent = custom_user_agent + return client + + +def make_async_client( + allow_preview: bool = True, + console_logging: bool = False, + custom_user_agent: Optional[str] = None, +) -> AsyncAIProjectClient: + """Return a minimal async AIProjectClient stub suitable for unit-testing get_openai_client.""" + client = AsyncAIProjectClient.__new__(AsyncAIProjectClient) + client._config = MagicMock() + client._config.endpoint = ENDPOINT + client._config.allow_preview = allow_preview + client._config.api_version = API_VERSION + client._config.credential = MagicMock() + client._console_logging_enabled = console_logging + client._custom_user_agent = custom_user_agent + return client + + +def mock_openai(user_agent: str = "openai/1.0"): + """Return ``(mock_class, mock_instance)`` where ``mock_class`` acts as the OpenAI constructor.""" + instance = MagicMock() + instance.user_agent = user_agent + mock_cls = MagicMock(return_value=instance) + return mock_cls, instance diff --git a/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_endpoint.py b/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_endpoint.py index 87aecf0a94fa..be5d6f429040 100644 --- a/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_endpoint.py +++ b/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_endpoint.py @@ -4,13 +4,26 @@ # Licensed under the MIT License. # ------------------------------------ """ -Unit tests for verifying the base_url of the OpenAI client returned by AIProjectClient.get_openai_client(). +Unit tests for verifying the base_url, default_query (api-version) and Foundry feature +header behavior of the OpenAI client returned by AIProjectClient.get_openai_client(). No network calls are made. """ +from unittest.mock import patch + import pytest from azure.core.credentials import AccessToken from azure.ai.projects import AIProjectClient +from azure.ai.projects.models._patch import _FOUNDRY_FEATURES_HEADER_NAME + +from openai_test_helpers import ( + ENDPOINT, + API_VERSION, + SYNC_OPENAI_PATCH, + SYNC_TOKEN_PROVIDER_PATCH, + make_sync_client, + mock_openai, +) FAKE_ENDPOINT = "https://fake-account.services.ai.azure.com/api/projects/fake-project" AGENT_NAME = "fake-agent-name" @@ -59,3 +72,106 @@ def test_get_openai_client_with_agent_name_and_allow_preview(self): expected_base_url = FAKE_ENDPOINT.rstrip("/") + f"/agents/{AGENT_NAME}/endpoint/protocols/openai" assert str(openai_client.base_url).rstrip("/") == expected_base_url + + def test_trailing_slash_on_endpoint_is_stripped(self): + """Trailing slash on endpoint must not produce a double slash in the base URL.""" + client = make_sync_client() + client._config.endpoint = ENDPOINT + "/" + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client() + for c in mock_cls.call_args_list: + host = c.kwargs["base_url"].replace("https://", "") + assert "//" not in host + + +# =========================================================================== +# default_query / api-version injection branches +# =========================================================================== + + +class TestDefaultQueryBranches: + def test_no_agent_no_api_version_injected(self): + """Branch: no agent_name -> api-version is NOT injected into default_query.""" + client = make_sync_client() + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client() + for c in mock_cls.call_args_list: + assert "api-version" not in c.kwargs.get("default_query", {}) + + def test_agent_injects_api_version(self): + """Branch: agent_name + no caller api-version -> inject SDK api_version.""" + client = make_sync_client() + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client(agent_name="my-agent") + for c in mock_cls.call_args_list: + assert c.kwargs["default_query"]["api-version"] == API_VERSION + + def test_agent_does_not_override_caller_api_version(self): + """Branch: agent_name + caller-provided api-version -> keep caller value.""" + client = make_sync_client() + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client(agent_name="my-agent", default_query={"api-version": "caller-v"}) + for c in mock_cls.call_args_list: + assert c.kwargs["default_query"]["api-version"] == "caller-v" + + def test_caller_default_query_values_preserved(self): + """Caller-supplied default_query keys other than api-version are preserved.""" + client = make_sync_client() + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client(default_query={"foo": "bar"}) + for c in mock_cls.call_args_list: + assert c.kwargs["default_query"]["foo"] == "bar" + + +# =========================================================================== +# Foundry feature header injection branches +# =========================================================================== + + +class TestFoundryFeatureHeaderBranches: + def test_no_agent_no_feature_header_injected(self): + """Branch: no agent_name -> Foundry feature header is NOT injected.""" + + client = make_sync_client() + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client() + real_call = mock_cls.call_args_list[-1] + assert _FOUNDRY_FEATURES_HEADER_NAME not in real_call.kwargs.get("default_headers", {}) + + def test_agent_injects_foundry_feature_header(self): + """Branch: agent_name + header not present -> inject the Foundry feature header.""" + + client = make_sync_client() + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client(agent_name="my-agent") + real_call = mock_cls.call_args_list[-1] + assert _FOUNDRY_FEATURES_HEADER_NAME in real_call.kwargs.get("default_headers", {}) + + def test_agent_does_not_override_existing_feature_header(self): + """Branch: agent_name + header already in caller headers -> keep caller value.""" + + client = make_sync_client() + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client( + agent_name="my-agent", + default_headers={_FOUNDRY_FEATURES_HEADER_NAME: "caller-value"}, + ) + real_call = mock_cls.call_args_list[-1] + assert real_call.kwargs["default_headers"][_FOUNDRY_FEATURES_HEADER_NAME] == "caller-value" + + def test_caller_headers_other_keys_preserved(self): + """Caller-supplied non-feature headers are passed through to the OpenAI client.""" + client = make_sync_client() + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client(default_headers={"X-My-Header": "hello"}) + real_call = mock_cls.call_args_list[-1] + assert real_call.kwargs["default_headers"]["X-My-Header"] == "hello" diff --git a/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_endpoint_async.py b/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_endpoint_async.py index f489a4f982b6..8335531787af 100644 --- a/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_endpoint_async.py +++ b/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_endpoint_async.py @@ -4,14 +4,27 @@ # Licensed under the MIT License. # ------------------------------------ """ -Unit tests for verifying the base_url of the AsyncOpenAI client returned by AIProjectClient.get_openai_client(). +Unit tests for verifying the base_url, default_query (api-version) and Foundry feature +header behavior of the AsyncOpenAI client returned by AIProjectClient.get_openai_client(). No network calls are made. """ -import pytest from typing import Any +from unittest.mock import patch + +import pytest from azure.core.credentials_async import AsyncTokenCredential from azure.ai.projects.aio import AIProjectClient +from azure.ai.projects.models._patch import _FOUNDRY_FEATURES_HEADER_NAME + +from openai_test_helpers import ( + ENDPOINT, + API_VERSION, + ASYNC_OPENAI_PATCH, + ASYNC_TOKEN_PROVIDER_PATCH, + make_async_client, + mock_openai, +) FAKE_ENDPOINT = "https://fake-account.services.ai.azure.com/api/projects/fake-project" AGENT_NAME = "fake-agent-name" @@ -68,3 +81,95 @@ async def test_get_openai_client_with_agent_name_and_allow_preview_async(self): expected_base_url = FAKE_ENDPOINT.rstrip("/") + f"/agents/{AGENT_NAME}/endpoint/protocols/openai" assert str(openai_client.base_url).rstrip("/") == expected_base_url + + @pytest.mark.asyncio + async def test_trailing_slash_on_endpoint_is_stripped_async(self): + """Trailing slash on endpoint must not produce a double slash in the base URL.""" + client = make_async_client() + client._config.endpoint = ENDPOINT + "/" + mock_cls, _ = mock_openai() + with patch(ASYNC_OPENAI_PATCH, mock_cls), patch(ASYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client() + for c in mock_cls.call_args_list: + host = c.kwargs["base_url"].replace("https://", "") + assert "//" not in host + + +# =========================================================================== +# default_query / api-version injection branches (async) +# =========================================================================== + + +class TestDefaultQueryBranchesAsync: + @pytest.mark.asyncio + async def test_no_agent_no_api_version_injected(self): + """Branch: no agent_name -> api-version is NOT injected into default_query.""" + client = make_async_client() + mock_cls, _ = mock_openai() + with patch(ASYNC_OPENAI_PATCH, mock_cls), patch(ASYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client() + for c in mock_cls.call_args_list: + assert "api-version" not in c.kwargs.get("default_query", {}) + + @pytest.mark.asyncio + async def test_agent_injects_api_version(self): + """Branch: agent_name + no caller api-version -> inject SDK api_version.""" + client = make_async_client() + mock_cls, _ = mock_openai() + with patch(ASYNC_OPENAI_PATCH, mock_cls), patch(ASYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client(agent_name="my-agent") + for c in mock_cls.call_args_list: + assert c.kwargs["default_query"]["api-version"] == API_VERSION + + @pytest.mark.asyncio + async def test_agent_does_not_override_caller_api_version(self): + """Branch: agent_name + caller-provided api-version -> keep caller value.""" + client = make_async_client() + mock_cls, _ = mock_openai() + with patch(ASYNC_OPENAI_PATCH, mock_cls), patch(ASYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client(agent_name="my-agent", default_query={"api-version": "caller-v"}) + for c in mock_cls.call_args_list: + assert c.kwargs["default_query"]["api-version"] == "caller-v" + + +# =========================================================================== +# Foundry feature header injection branches (async) +# =========================================================================== + + +class TestFoundryFeatureHeaderBranchesAsync: + @pytest.mark.asyncio + async def test_no_agent_no_feature_header_injected(self): + """Branch: no agent_name -> Foundry feature header is NOT injected.""" + + client = make_async_client() + mock_cls, _ = mock_openai() + with patch(ASYNC_OPENAI_PATCH, mock_cls), patch(ASYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client() + real_call = mock_cls.call_args_list[-1] + assert _FOUNDRY_FEATURES_HEADER_NAME not in real_call.kwargs.get("default_headers", {}) + + @pytest.mark.asyncio + async def test_agent_injects_foundry_feature_header(self): + """Branch: agent_name + header not present -> inject the Foundry feature header.""" + + client = make_async_client() + mock_cls, _ = mock_openai() + with patch(ASYNC_OPENAI_PATCH, mock_cls), patch(ASYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client(agent_name="my-agent") + real_call = mock_cls.call_args_list[-1] + assert _FOUNDRY_FEATURES_HEADER_NAME in real_call.kwargs.get("default_headers", {}) + + @pytest.mark.asyncio + async def test_agent_does_not_override_existing_feature_header(self): + """Branch: agent_name + header already in caller headers -> keep caller value.""" + + client = make_async_client() + mock_cls, _ = mock_openai() + with patch(ASYNC_OPENAI_PATCH, mock_cls), patch(ASYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client( + agent_name="my-agent", + default_headers={_FOUNDRY_FEATURES_HEADER_NAME: "caller-value"}, + ) + real_call = mock_cls.call_args_list[-1] + assert real_call.kwargs["default_headers"][_FOUNDRY_FEATURES_HEADER_NAME] == "caller-value" diff --git a/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_overrides.py b/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_overrides.py index 782c5024f43a..e0895ae552ca 100644 --- a/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_overrides.py +++ b/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_overrides.py @@ -4,17 +4,27 @@ # Licensed under the MIT License. # ------------------------------------ """ -Tests to verify that a custom http_client can be passed to get_openai_client() -and that the returned OpenAI client uses it instead of the default one. +Tests covering caller-side overrides (http_client, api_key, base_url, default_headers) +and the user-agent / token-provider / logging-transport branches of +AIProjectClient.get_openai_client() (sync). """ import os from typing import Any +from unittest.mock import patch + import pytest import httpx from azure.core.credentials import TokenCredential from azure.ai.projects import AIProjectClient +from openai_test_helpers import ( + SYNC_OPENAI_PATCH, + SYNC_TOKEN_PROVIDER_PATCH, + make_sync_client, + mock_openai, +) + class DummyTokenCredential(TokenCredential): """A dummy credential that returns None for testing purposes.""" @@ -134,3 +144,60 @@ def test_api_key_and_base_url_overrides(self): assert ( str(openai_client.base_url) == custom_base_url + "/" ), f"Expected base_url '{custom_base_url}/', got '{openai_client.base_url}'" + + +# =========================================================================== +# api_key resolution branches +# =========================================================================== + + +class TestApiKeyBranches: + def test_token_provider_used_when_no_api_key(self): + """Branch: no 'api_key' kwarg -> get_bearer_token_provider() is invoked.""" + client = make_sync_client() + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="provider") as mock_tp: + client.get_openai_client() + mock_tp.assert_called_once_with(client._config.credential, "https://ai.azure.com/.default") + for c in mock_cls.call_args_list: + assert c.kwargs["api_key"] == "provider" + + def test_caller_api_key_skips_token_provider(self): + """Branch: 'api_key' in kwargs -> token provider is NOT called.""" + client = make_sync_client() + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH) as mock_tp: + client.get_openai_client(api_key="my-secret-key") + mock_tp.assert_not_called() + for c in mock_cls.call_args_list: + assert c.kwargs["api_key"] == "my-secret-key" + + +# =========================================================================== +# http_client resolution branches +# =========================================================================== + + +class TestHttpClientBranches: + def test_http_client_is_none_by_default(self): + """Branch: no override + console logging off -> http_client is None.""" + client = make_sync_client(console_logging=False) + mock_cls, _ = mock_openai() + with patch(SYNC_OPENAI_PATCH, mock_cls), patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client() + for c in mock_cls.call_args_list: + assert c.kwargs["http_client"] is None + + def test_console_logging_creates_logging_transport(self): + """Branch: no override + _console_logging_enabled=True -> httpx.Client with logging transport.""" + client = make_sync_client(console_logging=True) + mock_cls, _ = mock_openai() + with ( + patch(SYNC_OPENAI_PATCH, mock_cls), + patch(SYNC_TOKEN_PROVIDER_PATCH, return_value="tok"), + patch("azure.ai.projects._patch.httpx") as mock_httpx, + patch("azure.ai.projects._patch._OpenAILoggingTransport"), + ): + mock_httpx.Client.return_value = object() + client.get_openai_client() + mock_httpx.Client.assert_called_once() diff --git a/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_overrides_async.py b/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_overrides_async.py index de8c484fd9f6..220dc961d22a 100644 --- a/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_overrides_async.py +++ b/sdk/ai/azure-ai-projects/tests/responses/test_openai_client_overrides_async.py @@ -4,17 +4,27 @@ # Licensed under the MIT License. # ------------------------------------ """ -Tests to verify that a custom http_client can be passed to get_openai_client() -and that the returned AsyncOpenAI client uses it instead of the default one. +Tests covering caller-side overrides (http_client, api_key, base_url, default_headers) +and the user-agent / token-provider / logging-transport branches of +AIProjectClient.get_openai_client() (async). """ import os from typing import Any +from unittest.mock import patch + import pytest import httpx from azure.core.credentials_async import AsyncTokenCredential from azure.ai.projects.aio import AIProjectClient +from openai_test_helpers import ( + ASYNC_OPENAI_PATCH, + ASYNC_TOKEN_PROVIDER_PATCH, + make_async_client, + mock_openai, +) + class DummyAsyncTokenCredential(AsyncTokenCredential): """A dummy async credential that returns None for testing purposes.""" @@ -138,3 +148,67 @@ async def test_api_key_and_base_url_overrides_async(self): assert ( str(openai_client.base_url) == custom_base_url + "/" ), f"Expected base_url '{custom_base_url}/', got '{openai_client.base_url}'" + + +# =========================================================================== +# api_key resolution branches (async) +# =========================================================================== + + +class TestApiKeyBranchesAsync: + @pytest.mark.asyncio + async def test_token_provider_used_when_no_api_key(self): + """Branch: no 'api_key' kwarg -> get_bearer_token_provider() is invoked.""" + client = make_async_client() + mock_cls, _ = mock_openai() + with ( + patch(ASYNC_OPENAI_PATCH, mock_cls), + patch(ASYNC_TOKEN_PROVIDER_PATCH, return_value="async-provider") as mock_tp, + ): + client.get_openai_client() + mock_tp.assert_called_once_with(client._config.credential, "https://ai.azure.com/.default") + for c in mock_cls.call_args_list: + assert c.kwargs["api_key"] == "async-provider" + + @pytest.mark.asyncio + async def test_caller_api_key_skips_token_provider(self): + """Branch: 'api_key' in kwargs -> token provider is NOT called.""" + client = make_async_client() + mock_cls, _ = mock_openai() + with patch(ASYNC_OPENAI_PATCH, mock_cls), patch(ASYNC_TOKEN_PROVIDER_PATCH) as mock_tp: + client.get_openai_client(api_key="async-secret") + mock_tp.assert_not_called() + for c in mock_cls.call_args_list: + assert c.kwargs["api_key"] == "async-secret" + + +# =========================================================================== +# http_client resolution branches (async) +# =========================================================================== + + +class TestHttpClientBranchesAsync: + @pytest.mark.asyncio + async def test_http_client_is_none_by_default(self): + """Branch: no override + console logging off -> http_client is None.""" + client = make_async_client(console_logging=False) + mock_cls, _ = mock_openai() + with patch(ASYNC_OPENAI_PATCH, mock_cls), patch(ASYNC_TOKEN_PROVIDER_PATCH, return_value="tok"): + client.get_openai_client() + for c in mock_cls.call_args_list: + assert c.kwargs["http_client"] is None + + @pytest.mark.asyncio + async def test_console_logging_creates_async_logging_transport(self): + """Branch: no override + _console_logging_enabled=True -> httpx.AsyncClient with logging transport.""" + client = make_async_client(console_logging=True) + mock_cls, _ = mock_openai() + with ( + patch(ASYNC_OPENAI_PATCH, mock_cls), + patch(ASYNC_TOKEN_PROVIDER_PATCH, return_value="tok"), + patch("azure.ai.projects.aio._patch.httpx") as mock_httpx, + patch("azure.ai.projects.aio._patch._OpenAILoggingTransport"), + ): + mock_httpx.AsyncClient.return_value = object() + client.get_openai_client() + mock_httpx.AsyncClient.assert_called_once() diff --git a/sdk/ai/azure-ai-projects/tests/samples/sample_executor.py b/sdk/ai/azure-ai-projects/tests/samples/sample_executor.py index 4c7d990c2975..9fc9adfa3698 100644 --- a/sdk/ai/azure-ai-projects/tests/samples/sample_executor.py +++ b/sdk/ai/azure-ai-projects/tests/samples/sample_executor.py @@ -242,9 +242,15 @@ def _capture_debug_logs(self): """Capture logger DEBUG output into the same array used for print capture.""" bearer_token_pattern = re.compile(r"(?i)(Bearer\s+)([^\s\"',;]+)") + # Matches raw JWT-like tokens assigned to an "authorization" JSON field + # (e.g., when a sample passes `authorization=token` to MCPTool, the request + # body is logged as `"authorization": "eyJ..."` without a "Bearer " prefix). + json_authorization_pattern = re.compile(r"(?i)(\"authorization\"\s*:\s*\")([^\"]+)(\")") def _sanitize_log_message(message: str) -> str: - return bearer_token_pattern.sub(r"\1", message) + message = bearer_token_pattern.sub(r"\1", message) + message = json_authorization_pattern.sub(r"\1\3", message) + return message class _PrintCaptureLogHandler(logging.Handler): def __init__(self, sink: list[str]): @@ -1044,13 +1050,11 @@ def _resolve_additional_env_vars( resolved: dict[str, str] = {} if _is_live_mode(): - for env_key, _ in playback_values.items(): + for env_key, playback_value in playback_values.items(): live_value = os.environ.get(env_key) if not live_value: - raise ValueError( - f"Missing required environment variable '{env_key}' for live recording of sample '{sample_filename}'. " - "Either set it in your environment/.env file or run in playback mode." - ) + resolved[env_key] = playback_value + continue resolved[env_key] = live_value else: resolved.update(playback_values) 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 4ae4f4010925..598e9e3e564e 100644 --- a/sdk/ai/azure-ai-projects/tests/samples/test_samples.py +++ b/sdk/ai/azure-ai-projects/tests/samples/test_samples.py @@ -6,7 +6,7 @@ import pytest import os from devtools_testutils import recorded_by_proxy, AzureRecordedTestCase, RecordedTransport -from test_base import servicePreparer, fineTuningServicePreparer +from test_base import servicePreparer, fineTuningServicePreparer, modelsServicePreparer from sample_executor import ( AdditionalSampleTestDetail, SyncSampleExecutor, @@ -85,6 +85,7 @@ def test_memory_samples(self, sample_path: str, **kwargs) -> None: get_sample_paths( "agents", samples_to_skip=[ + "sample_external_agents_crud.py", # Skipped until recordings are available. "sample_workflow_multi_agent.py", # No issue to run. Just postpone recording. "sample_workflow_multi_agent_with_mcp_approval.py", # No issue to run. Just postpone recording. ], @@ -152,11 +153,63 @@ def test_deployments_samples(self, sample_path: str, **kwargs) -> None: @pytest.mark.parametrize( "sample_path", get_sample_paths( - "datasets", - samples_to_skip=[], + "models", + samples_to_test=[ + # `sample_models_basic.py` uses the `create()` helper which shells out + # to AzCopy. AzCopy traffic isn't captured by the test proxy, so the + # sample can't be replayed from a recording. Live re-recording is still + # exercised via the standalone tests in `tests/models/`. + "sample_models_create_and_poll.py", + ], ), ) + @modelsServicePreparer() + @SamplePathPasser() + @recorded_by_proxy(RecordedTransport.AZURE_CORE, RecordedTransport.HTTPX) + def test_models_samples(self, sample_path: str, **kwargs) -> None: + import secrets # local import to avoid module-level dep + + env_vars = get_sample_env_vars(kwargs) + # Foundry permanently reserves a `/` asset namespace even + # after `models.delete`, so every live re-recording needs a unique name. + # Sanitize back to a stable value in conftest so playback URLs match. + suffix = secrets.token_hex(4) if self.is_live else "00000000" + env_vars["MODEL_NAME"] = f"recsmplmdl{suffix}" + env_vars["MODEL_VERSION"] = "1" + executor = SyncSampleExecutor(self, sample_path, env_vars=env_vars, **kwargs) + executor.execute() + # `validate_print_calls_by_llm` is intentionally not called: it requires + # an Azure OpenAI connection on the Foundry project, which the canary + # project used for `.beta.models` recordings does not have. The sample + # is still validated end-to-end by `executor.execute()` (any exception + # fails the test). + @servicePreparer() + # @additionalSampleTests( + # [ + # AdditionalSampleTestDetail( + # test_id="sample_dataset_generation_job_simpleqna_with_prompt_source", + # sample_filename="sample_dataset_generation_job_simpleqna_with_prompt_source.py", + # env_vars={ + # "POLL_INTERVAL_SECONDS": "60", + # }, + # ), + # ] + # ) + @pytest.mark.parametrize( + "sample_path", + get_sample_paths( + "datasets", + samples_to_skip=[ + "sample_dataset_generation_job_simpleqna_with_prompt_source.py", # PR #47067: recording not yet available + "sample_dataset_generation_job_traces_for_finetuning.py", # PR #47067: recording not yet available + "sample_dataset_generation_job_simpleqna_for_finetuning.py", # PR #47067: recording not yet available + "sample_dataset_generation_job_traces_for_evaluation.py", # PR #47067: recording not yet available + "sample_dataset_generation_job_simpleqna_with_agent_source.py", # PR #47067: recording not yet available + "sample_dataset_generation_job_simpleqna_with_file_source.py", # PR #47067: recording not yet available + ], + ), + ) @SamplePathPasser() @recorded_by_proxy(RecordedTransport.AZURE_CORE, RecordedTransport.HTTPX) def test_datasets_samples(self, sample_path: str, **kwargs) -> None: @@ -181,6 +234,18 @@ def test_chat_completions_samples(self, sample_path: str, **kwargs) -> None: executor.execute() executor.validate_print_calls_by_llm() + @servicePreparer() + @additionalSampleTests( + [ + AdditionalSampleTestDetail( + test_id="sample_create_hosted_agent_from_remote_build", + sample_filename="sample_create_hosted_agent_from_code.py", + env_vars={ + "FOUNDRY_HOSTED_AGENT_REMOTE_BUILD": "true", + }, + ), + ] + ) @pytest.mark.parametrize( "sample_path", get_sample_paths( @@ -188,10 +253,11 @@ def test_chat_completions_samples(self, sample_path: str, **kwargs) -> None: samples_to_skip=[], ), ) - @servicePreparer() @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() diff --git a/sdk/ai/azure-ai-projects/tests/samples/test_samples_async.py b/sdk/ai/azure-ai-projects/tests/samples/test_samples_async.py index 1c742537c5fc..e3b76dacf469 100644 --- a/sdk/ai/azure-ai-projects/tests/samples/test_samples_async.py +++ b/sdk/ai/azure-ai-projects/tests/samples/test_samples_async.py @@ -3,13 +3,15 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -import pytest +import pytest, os from devtools_testutils.aio import recorded_by_proxy_async from devtools_testutils import AzureRecordedTestCase, RecordedTransport -from test_base import servicePreparer +from test_base import servicePreparer, modelsServicePreparer from sample_executor import ( + AdditionalSampleTestDetail, AsyncSampleExecutor, SamplePathPasser, + additionalSampleTests, get_async_sample_paths, ) from test_samples_helpers import get_sample_env_vars @@ -69,7 +71,10 @@ async def test_memory_samples(self, sample_path: str, **kwargs) -> None: "sample_path", get_async_sample_paths( "agents", - samples_to_skip=["sample_workflow_multi_agent_async.py"], + samples_to_skip=[ + "sample_external_agents_crud_async.py", # Skipped until recordings are available. + "sample_workflow_multi_agent_async.py", + ], ), ) @servicePreparer() @@ -133,6 +138,33 @@ async def test_deployments_samples(self, sample_path: str, **kwargs) -> None: await executor.execute_async() await executor.validate_print_calls_by_llm_async() + @pytest.mark.parametrize( + "sample_path", + get_async_sample_paths( + "models", + samples_to_test=[ + "sample_models_basic_async.py", + ], + ), + ) + @modelsServicePreparer() + @SamplePathPasser() + @recorded_by_proxy_async(RecordedTransport.AZURE_CORE, RecordedTransport.HTTPX) + async def test_models_samples(self, sample_path: str, **kwargs) -> None: + import secrets # local import to avoid module-level dep + + env_vars = get_sample_env_vars(kwargs) + # Foundry permanently reserves a `/` asset namespace even + # after `models.delete`, so every live re-recording needs a unique name. + # Sanitize back to a stable value in conftest so playback URLs match. + suffix = secrets.token_hex(4) if self.is_live else "00000000" + env_vars["MODEL_NAME"] = f"recsmplmdl{suffix}" + env_vars["MODEL_VERSION"] = "1" + executor = AsyncSampleExecutor(self, sample_path, env_vars=env_vars, **kwargs) + await executor.execute_async() + # `validate_print_calls_by_llm_async` is intentionally not called: see + # the comment on the synchronous `test_models_samples` for details. + @pytest.mark.parametrize( "sample_path", get_async_sample_paths( @@ -170,6 +202,18 @@ async def test_chat_completions_samples(self, sample_path: str, **kwargs) -> Non await executor.execute_async() await executor.validate_print_calls_by_llm_async() + @servicePreparer() + @additionalSampleTests( + [ + AdditionalSampleTestDetail( + test_id="sample_create_hosted_agent_from_remote_build_async", + sample_filename="sample_create_hosted_agent_from_code_async.py", + env_vars={ + "FOUNDRY_HOSTED_AGENT_REMOTE_BUILD": "true", + }, + ), + ] + ) @pytest.mark.parametrize( "sample_path", get_async_sample_paths( @@ -177,10 +221,11 @@ async def test_chat_completions_samples(self, sample_path: str, **kwargs) -> Non samples_to_skip=[], ), ) - @servicePreparer() @SamplePathPasser() @recorded_by_proxy_async(RecordedTransport.AZURE_CORE, RecordedTransport.HTTPX) async 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 = AsyncSampleExecutor(self, sample_path, env_vars=env_vars, **kwargs) await executor.execute_async() diff --git a/sdk/ai/azure-ai-projects/tests/samples/test_samples_evaluations.py b/sdk/ai/azure-ai-projects/tests/samples/test_samples_evaluations.py index 2618d0320a05..65d8a94d5d4e 100644 --- a/sdk/ai/azure-ai-projects/tests/samples/test_samples_evaluations.py +++ b/sdk/ai/azure-ai-projects/tests/samples/test_samples_evaluations.py @@ -152,6 +152,15 @@ class TestSamplesEvaluations(AzureRecordedTestCase): "sample_synthetic_data_agent_evaluation.py", # Synthetic data gen is long-running preview feature "sample_synthetic_data_model_evaluation.py", # Synthetic data gen is long-running preview feature "sample_eval_catalog_prompt_based_evaluators.py", # For some reason fails with 500 (Internal server error) + "sample_multiturn_conversation_evaluation.py", # PR #47034: new multi-turn sample, recording not yet available + "sample_multiturn_conversation_simulation.py", # PR #47034: new multi-turn sample, recording not yet available + "sample_multiturn_trace_evaluation_agent_filter.py", # PR #47034: new multi-turn sample, recording not yet available + "sample_multiturn_trace_evaluation_by_id.py", # PR #47034: new multi-turn sample, recording not yet available + "sample_rubric_evaluator_generation_all_sources.py", # PR #47057: recording not yet available + "sample_rubric_evaluator_generation_lifecycle.py", # PR #47057: recording not yet available + "sample_rubric_evaluator_generation_basic.py", # PR #47057: recording not yet available + "sample_rubric_evaluator_manual.py", # PR #47057: recording not yet available + "sample_rubric_evaluator_generation_iterate.py", # PR #47057: recording not yet available ], ), ) diff --git a/sdk/ai/azure-ai-projects/tests/test_base.py b/sdk/ai/azure-ai-projects/tests/test_base.py index 0979f5a177b6..5b69b45ce21a 100644 --- a/sdk/ai/azure-ai-projects/tests/test_base.py +++ b/sdk/ai/azure-ai-projects/tests/test_base.py @@ -54,6 +54,7 @@ mcp_project_connection_id="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/sanitized-resource-group/providers/Microsoft.CognitiveServices/accounts/sanitized-account/projects/sanitized-project/connections/sanitized-mcp-connection", browser_automation_project_connection_id="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/sanitized-resource-group/providers/Microsoft.CognitiveServices/accounts/sanitized-account/projects/sanitized-project/connections/sanitized-browser-automation-connection", sharepoint_project_connection_id="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/sanitized-resource-group/providers/Microsoft.CognitiveServices/accounts/sanitized-account/projects/sanitized-project/connections/sanitized-sharepoint-connection", + fabric_iq_project_connection_id="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/sanitized-resource-group/providers/Microsoft.CognitiveServices/accounts/sanitized-account/projects/sanitized-project/connections/sanitized-fabric-iq-connection", bing_custom_search_instance_name="sanitized-bing-custom-search-instance", completed_oai_model_sft_fine_tuning_job_id="sanitized-ftjob-id", completed_oai_model_rft_fine_tuning_job_id="sanitized-ftjob-id", @@ -68,9 +69,11 @@ fabric_user_input="List all customers!", a2a_user_input="What can the secondary agent do?", bing_custom_user_input="Tell me more about foundry agent service", + fabric_iq_user_input="Tell me weather history in London, Ohio", memory_store_chat_model_deployment_name="sanitized-model-deployment-name", memory_store_embedding_model_deployment_name="text-embedding-ada-002", foundry_agent_container_image="sanitizedregistry.azurecr.io/sanitized/sessions-agent:latest", + foundry_hosted_agent_name="sanitized-hosted-agent-name", ) fineTuningServicePreparer = functools.partial( @@ -84,6 +87,16 @@ azure_ai_projects_azure_aoai_account="sanitized-aoai-account", ) +# Slim preparer for `.beta.models` samples/tests. These exercise local-file +# upload + ModelVersion registration; they only need a Foundry project endpoint +# and the LLM-validation endpoint used by sample tests. +modelsServicePreparer = functools.partial( + EnvironmentVariableLoader, + "", + foundry_project_endpoint="https://sanitized-account-name.services.ai.azure.com/api/projects/sanitized-project-name", + llm_validation_project_endpoint="https://sanitized-account-name.services.ai.azure.com/api/projects/sanitized-project-name", +) + # Fine-tuning job type constants SFT_JOB_TYPE: Final[str] = "sft" DPO_JOB_TYPE: Final[str] = "dpo" @@ -203,6 +216,12 @@ class TestBase(AzureRecordedTestCase): "connection_name": "balapvbyostoragecanary", } + test_models_params = { + "model_name_1": f"test-model-name-{random.randint(0, 99999):05d}", + "model_name_2": f"test-model-name-{random.randint(0, 99999):05d}", + "model_version": "1", + } + test_files_params = { "test_file_name": "test_file.jsonl", "file_purpose": "fine-tune", diff --git a/sdk/ai/azure-ai-projects/tests/test_data/models/config.json b/sdk/ai/azure-ai-projects/tests/test_data/models/config.json new file mode 100644 index 000000000000..a60e008113c0 --- /dev/null +++ b/sdk/ai/azure-ai-projects/tests/test_data/models/config.json @@ -0,0 +1 @@ +{"sample": true} diff --git a/sdk/ai/azure-ai-projects/tests/test_data/models/weights.bin b/sdk/ai/azure-ai-projects/tests/test_data/models/weights.bin new file mode 100644 index 000000000000..06a01532107f --- /dev/null +++ b/sdk/ai/azure-ai-projects/tests/test_data/models/weights.bin @@ -0,0 +1 @@ +test-weight-bytes diff --git a/sdk/ai/azure-ai-projects/tsp-location-omitted.yaml b/sdk/ai/azure-ai-projects/tsp-location.yaml similarity index 69% rename from sdk/ai/azure-ai-projects/tsp-location-omitted.yaml rename to sdk/ai/azure-ai-projects/tsp-location.yaml index 70b8d1cf4350..8ba9d2724740 100644 --- a/sdk/ai/azure-ai-projects/tsp-location-omitted.yaml +++ b/sdk/ai/azure-ai-projects/tsp-location.yaml @@ -1,4 +1,4 @@ directory: specification/ai-foundry/data-plane/Foundry -commit: 9f045138c73bf7aa13ee59b805cd9143dbb77835 +commit: b9aae18b683e7d605be75b6b77a0397cd3bd2a4a repo: Azure/azure-rest-api-specs additionalDirectories: