diff --git a/sdk/ai/azure-ai-projects/.env.template b/sdk/ai/azure-ai-projects/.env.template index a89bd766f143..f56430b5db08 100644 --- a/sdk/ai/azure-ai-projects/.env.template +++ b/sdk/ai/azure-ai-projects/.env.template @@ -60,6 +60,8 @@ OPTIMIZATION_MODEL= JOB_ID= WORK_IQ_PROJECT_CONNECTION_ID= WORK_IQ_USER_INPUT= +WEB_IQ_PROJECT_CONNECTION_ID= +WEB_IQ_USER_INPUT= FABRIC_IQ_PROJECT_CONNECTION_ID= FABRIC_IQ_USER_INPUT= AI_SEARCH_CONNECTION_NAME= diff --git a/sdk/ai/azure-ai-projects/.github/skills/README.md b/sdk/ai/azure-ai-projects/.github/skills/README.md index c6cb95b0658c..85e1998c07d0 100644 --- a/sdk/ai/azure-ai-projects/.github/skills/README.md +++ b/sdk/ai/azure-ai-projects/.github/skills/README.md @@ -49,7 +49,10 @@ This skill creates a new topic branch, emits SDK from TypeSpec, runs some post-p This skill updates the file CHANGELOG.md, comparing the source in the current branch which the source of the latest public release. It does not create a new topic branch or a PR. +### azure-ai-projects-author-samples +This skill compares a newly emitted or merged public API surface with its selected base, then creates or updates idiomatic synchronous and asynchronous Python samples. It also keeps unrecorded samples excluded from the recorded sample harness. +### azure-ai-projects-author-tests - +This skill updates existing pytest coverage and authors complete sync/async Test Proxy tests for new behavior. New recorded service tests remain explicitly skipped until a human adds recordings. diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/SKILL.md b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/SKILL.md new file mode 100644 index 000000000000..f2c8d7496749 --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/SKILL.md @@ -0,0 +1,59 @@ +--- +name: azure-ai-projects-author-samples +description: 'Create or update idiomatic azure-ai-projects Python samples for newly emitted or merged API changes. WHEN: author azure-ai-projects samples; update azure-ai-projects samples after TypeSpec emission; add azure-ai-projects feature samples; fix azure-ai-projects samples after API changes. DO NOT USE FOR: other packages; Test Proxy recording. INVOKES: git, azpysdk, Python validation commands.' +--- + +# Author samples for azure-ai-projects API changes + +Run from `sdk\ai\azure-ai-projects`. This workflow handles both uncommitted emission output and already-merged changes. + +## 1. Establish the API delta + +Regenerate `api.md` with `azpysdk apistub .`. Choose the comparison base explicitly: + +```powershell +# Uncommitted emission/customization changes +git diff HEAD -- api.md azure\ai\projects + +# Committed or merged changes +git diff ...HEAD -- api.md azure\ai\projects +``` + +Use [prompts\diff-api-surface.prompt.md](prompts/diff-api-surface.prompt.md) to classify additions, signature changes, renames, and removals. Do not use the latest release as the base when the requested scope is a particular emission or merge. + +## 2. Map changes to existing coverage + +Search `samples\` and `tests\` for every affected public symbol. Update all stale call sites for renames, removals, and changed parameters. Add a new sample only for a meaningful end-to-end workflow, not for every new model or overload. + +Bucket work beside the closest existing feature. Read the neighboring sync/async pair before editing; see [references\sample-conventions.md](references/sample-conventions.md). + +## 3. Author or update samples + +- Keep sync and async samples behaviorally equivalent when both clients expose the feature. +- Start new files from [templates\sample-skeleton.py](templates/sample-skeleton.py) and [templates\sample-skeleton_async.py](templates/sample-skeleton_async.py), then replace every placeholder. +- Use only public imports and the final customized API surface from `api.md`; never import generated internals. +- Make the workflow runnable against a real Foundry project, narrate useful results, and clean up created resources in `finally` where failure could leak them. +- Preserve nearby sample voice, environment-variable names, authentication style, and preview wording. Python samples already cover beta APIs, so do not exclude a feature merely because it is under `.beta`. + +## 4. Keep recorded sample tests green + +Check `tests\samples\test_samples.py` and `test_samples_async.py`. Existing folders are often auto-discovered by `get_sample_paths` or `get_async_sample_paths`; when no recording exists, add each new filename to that test's `samples_to_skip` with a concrete recording-needed reason. For a new sample folder, add matching sync/async harness coverage but keep the new cases excluded until recordings are supplied. + +Do not create recordings or modify `assets.json`. + +## 5. Validate + +Run targeted syntax and formatting checks for every edited sample, then package checks: + +```powershell +python -m compileall -q +python -m black --check +azpysdk pylint . +azpysdk mypy . +``` + +Fix source problems; do not weaken lint or type-check settings. Do not run samples live unless the user explicitly requests it. + +## 6. Hand off + +Run `azure-ai-projects-author-tests` for affected behavior, then `azure-ai-projects-update-changelog`. diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/prompts/diff-api-surface.prompt.md b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/prompts/diff-api-surface.prompt.md new file mode 100644 index 000000000000..ab8f2a86e9c8 --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/prompts/diff-api-surface.prompt.md @@ -0,0 +1,74 @@ +# Diff the azure-ai-projects Python API surface + +Classify the public API changes introduced by one emission or merge. The report drives sample and test authoring. + +## Inputs + +- `api.md` at the current worktree or `HEAD`. +- `api.md` at the explicitly selected comparison base. +- The same-range diff under `azure\ai\projects` when `api.md` lacks enough context to distinguish a rename from add/remove. + +Use one range consistently: + +```powershell +# Uncommitted changes +git diff --unified=80 HEAD -- api.md azure\ai\projects + +# Committed or merged changes +git diff --unified=80 ...HEAD -- api.md azure\ai\projects +``` + +## Task + +Return additions, behaviorally relevant signature changes, likely renames, and removals: + +```json +{ + "addedNamespaces": [ + {"name": "azure.ai.projects.operations.ExampleOperations", "isBeta": false} + ], + "addedClasses": [ + {"name": "Example", "namespace": "azure.ai.projects.models", "isBeta": false} + ], + "addedMethods": [ + { + "name": "create", + "owner": "ExampleOperations", + "namespace": "azure.ai.projects.operations", + "signature": "create(name: str, *, description: str | None = ...) -> Example", + "isBeta": false + } + ], + "changedMethods": [ + { + "name": "update", + "owner": "ExampleOperations", + "before": "update(name: str, body: JSON) -> Example", + "after": "update(name: str, *, description: str) -> Example", + "changeKinds": ["parameter-removed", "keyword-added"], + "isBeta": false + } + ], + "renamedSymbols": [ + {"oldName": "OldExample", "newName": "Example", "kind": "class", "confidence": "high"} + ], + "removedSymbols": [ + {"name": "delete_legacy", "owner": "ExampleOperations", "kind": "method", "isBeta": false} + ], + "affectedAreas": ["examples"] +} +``` + +Rules: + +1. Include public surface only; exclude underscore-prefixed symbols and generated implementation details absent from `api.md`. +2. Collapse overload-only formatting noise. Report a method once with its effective public signatures. +3. Mark anything owned by `BetaOperations`, a `Beta*Operations` type, or exposed through `.beta` as `isBeta: true`. +4. Use source context and matching owners/shapes to identify likely renames. If uncertain, emit separate add/remove entries rather than claiming a rename. +5. Include changed defaults, required/optional transitions, sync/async exposure changes, and return-type changes. +6. Do not treat the expected sync/async duplicate as two product features. +7. Set `affectedAreas` to likely existing `samples\`/`tests\` feature folders; do not invent a folder when no close match exists. + +## Output + +Return only the JSON report. diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/references/sample-conventions.md b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/references/sample-conventions.md new file mode 100644 index 000000000000..57c5f2c3e0d5 --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/references/sample-conventions.md @@ -0,0 +1,29 @@ +# azure-ai-projects sample conventions + +Use nearby files as the final authority. Good anchors are [`sample_agent_basic.py`](../../../../samples/agents/sample_agent_basic.py), its [async mirror](../../../../samples/agents/sample_agent_basic_async.py), and the [skills CRUD pair](../../../../samples/skills/). + +## Shape and naming + +- Put `sample_.py` beside related features and use `sample__async.py` for the async mirror. +- Start with the package copyright header and a module docstring containing `DESCRIPTION`, `USAGE`, install prerequisites, and every required/optional environment variable. +- Load `.env` with `python-dotenv`; read required values with `os.environ[...]` and optional values with `os.environ.get(...)`. +- Import sync credentials/client from `azure.identity` and `azure.ai.projects`; async variants come from their `.aio` namespaces. Import models from the public `azure.ai.projects.models` surface. +- Prefer one coherent workflow with useful progress/result output. Iterate pageable results with `for` or `async for`, and `await` every async service call. + +## Lifetime and parity + +Use credential and client context managers. When creating service resources, retain identifiers and clean them up in `finally` so partial failures do not leak resources. Sync and async samples should demonstrate the same scenario, parameters, output, and cleanup unless the API exists in only one client. + +The sample executor imports the module and calls `main()` when present. A guarded sync `main()` and an `asyncio.run(main())` async entry point are both supported. Match the closest neighboring pair rather than restyling an existing sample unnecessarily. + +## Updating existing samples + +For a renamed/changed API, search every sample call site and update it in place. Preserve the scenario unless the old behavior no longer exists. Do not import `_operations`, `_models`, `_patch`, or other implementation modules to bypass the public surface. + +Python samples intentionally include preview features; state preview status in the description and use the existing `.beta` access pattern when applicable. + +## Recorded sample harness + +[`test_samples.py`](../../../../tests/samples/test_samples.py) and [`test_samples_async.py`](../../../../tests/samples/test_samples_async.py) auto-discover many folders. Add unrecorded files to the appropriate `samples_to_skip` list with a specific reason. Whitelist-based `samples_to_test` blocks require explicit opt-in instead. + +If a new folder needs output validation, also update [`llm_instructions.py`](../../../../tests/samples/llm_instructions.py) and its folder mapping. Add required sanitized environment defaults through the narrowest preparer and mapping helper; never put live values in source. diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/templates/sample-skeleton.py b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/templates/sample-skeleton.py new file mode 100644 index 000000000000..53528540e30f --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/templates/sample-skeleton.py @@ -0,0 +1,52 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + This sample demonstrates how to use with the synchronous + AIProjectClient. + +USAGE: + python sample_.py + + Before running the sample: + + pip install "azure-ai-projects" python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - The Microsoft Foundry project endpoint. +""" + +import os + +from dotenv import load_dotenv + +from azure.identity import DefaultAzureCredential +from azure.ai.projects import AIProjectClient + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] + + +def main() -> None: + created_resource = None + + with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + ): + try: + # TODO(): call the public operation and print meaningful results. + created_resource = project_client + print("Completed workflow") + finally: + if created_resource is not None: + # TODO(): delete service resources created by this sample. + pass + + +if __name__ == "__main__": + main() diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/templates/sample-skeleton_async.py b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/templates/sample-skeleton_async.py new file mode 100644 index 000000000000..1dcec3f82c4b --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-samples/templates/sample-skeleton_async.py @@ -0,0 +1,53 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + This sample demonstrates how to use with the asynchronous + AIProjectClient. + +USAGE: + python sample__async.py + + Before running the sample: + + pip install "azure-ai-projects" python-dotenv aiohttp + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - The Microsoft Foundry project endpoint. +""" + +import asyncio +import os + +from dotenv import load_dotenv + +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: + created_resource = None + + async with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + ): + try: + # TODO(): await the public operation and print meaningful results. + created_resource = project_client + print("Completed workflow") + finally: + if created_resource is not None: + # TODO(): await deletion of service resources created by this sample. + pass + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/SKILL.md b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/SKILL.md new file mode 100644 index 000000000000..ffe880ea7045 --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/SKILL.md @@ -0,0 +1,43 @@ +--- +name: azure-ai-projects-author-tests +description: 'Create or update idiomatic azure-ai-projects pytest coverage for newly emitted or merged API changes, leaving new service tests skipped until Test Proxy recordings exist. WHEN: author azure-ai-projects tests; update azure-ai-projects tests after TypeSpec emission; add azure-ai-projects feature tests; fix azure-ai-projects tests after API changes. DO NOT USE FOR: other packages; recording or rerecording tests. INVOKES: git, pytest collection, azpysdk, Python validation commands.' +--- + +# Author tests for azure-ai-projects API changes + +Run from `sdk\ai\azure-ai-projects`. Consume the API-delta report from `azure-ai-projects-author-samples`, or recompute it with [..\azure-ai-projects-author-samples\prompts\diff-api-surface.prompt.md](../azure-ai-projects-author-samples/prompts/diff-api-surface.prompt.md). + +## 1. Map the delta to tests + +Search `tests\` for every affected symbol. Update existing tests and assertions for changed signatures, renames, and removals while preserving their current enabled/skipped state. Add new behavior coverage beside the closest feature tests; Python coverage includes both GA and beta APIs. + +Prefer extending an existing sync/async pair. Read its fixtures, decorators, sanitizers, resource naming, assertions, and cleanup before writing code; see [references\test-conventions.md](references/test-conventions.md). + +## 2. Write complete, disabled new coverage + +Use [templates\test-skeleton.py](templates/test-skeleton.py) and [templates\test-skeleton_async.py](templates/test-skeleton_async.py) only as structural starting points. Replace every placeholder and write the actual service calls, assertions, and cleanup. + +- For a wholly new test file, put `@pytest.mark.skip(reason="TODO(): enable after Test Proxy recordings are added.")` on the class. +- When adding coverage to an active class, put that marker on each new test method only; never disable unrelated tests. +- Keep sync and async tests behaviorally equivalent when both APIs exist. +- Use `TestBase`, the narrowest existing preparer, `create_client`/`create_async_client`, and `recorded_by_proxy`/`recorded_by_proxy_async`. +- Add sanitized preparer values, function-scoped sanitizers, and files under `tests\test_data` only when the scenario requires them. Never place secrets or live resource identifiers in source. +- Use `RecordedTransport.HTTPX2` only when the path also calls an OpenAI/httpx client. Follow the package's passthrough-wrapper pattern when combining parametrization with recorded decorators. + +Do not add recordings and do not modify `assets.json`. A skipped test must still import and collect successfully. + +## 3. Validate without enabling new tests + +```powershell +python -m compileall -q +python -m black --check +pytest --collect-only -q +azpysdk pylint . +azpysdk mypy . +``` + +Run targeted playback for updated pre-existing tests only when recordings already exist. Do not run new service tests live and do not remove their skip markers. + +## 4. Hand off + +Report which new tests remain skipped and why, then run `azure-ai-projects-update-changelog`. diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/references/test-conventions.md b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/references/test-conventions.md new file mode 100644 index 000000000000..c5943b003627 --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/references/test-conventions.md @@ -0,0 +1,30 @@ +# azure-ai-projects test conventions + +Use the nearest feature tests as the final authority. Simple recorded anchors are [`test_deployments.py`](../../../../tests/deployments/test_deployments.py) and its [async mirror](../../../../tests/deployments/test_deployments_async.py); shared clients and preparers live in [`test_base.py`](../../../../tests/test_base.py). + +## Test kind + +- Keep deterministic model/customization tests as ordinary enabled unit tests when no service call or recording is needed. +- Service tests derive from `TestBase`, use `@servicePreparer()` (or a narrower existing preparer), and use `@recorded_by_proxy` or `@recorded_by_proxy_async`. +- Create clients with `self.create_client(**kwargs)` or `self.create_async_client(**kwargs)` so playback uses fake credentials and sanitized endpoints. Pass `allow_preview=True` only when the neighboring feature/API requires it. +- Mirror sync files/methods with `_async` naming and equivalent assertions/cleanup. + +## New coverage stays disabled + +A wholly new recorded file gets one class-level marker: + +```python +@pytest.mark.skip(reason="TODO(): enable after Test Proxy recordings are added.") +``` + +In an active class, mark only each new recorded method. Write the complete operation flow before applying the marker; skipped placeholders are not coverage. Updates to pre-existing recorded tests retain their current state. + +## Recording-safe design + +Use stable inputs where possible. For generated names/IDs/timestamps, add the narrowest function-scoped sanitizer or reuse a sanitizer in `tests\conftest.py`. Add sanitized defaults to the narrowest `EnvironmentVariableLoader` preparer. Never commit credentials, tokens, account names, or raw endpoints. + +Place reusable payloads under `tests\test_data`; preserve LF handling and `.gitattributes` patterns for uploaded text files. Use `try/finally` cleanup and assert returned IDs, names, states, paging behavior, and error contracts rather than only checking non-`None`. + +Include `RecordedTransport.HTTPX2` when the test also uses an OpenAI/httpx client. When combining `pytest.mark.parametrize` with recorder decorators, copy the package's passthrough-wrapper pattern described in [`test_finetuning.py`](../../../../tests/finetuning/test_finetuning.py). + +Do not edit `assets.json` or add recordings in this workflow. Collection must succeed while the new service tests remain skipped. diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/templates/test-skeleton.py b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/templates/test-skeleton.py new file mode 100644 index 000000000000..8b8ed045292f --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/templates/test-skeleton.py @@ -0,0 +1,29 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest + +from devtools_testutils import recorded_by_proxy + +from test_base import TestBase, servicePreparer + + +@pytest.mark.skip(reason="TODO(): enable after Test Proxy recordings are added.") +class TestFeature(TestBase): + + @servicePreparer() + @recorded_by_proxy + def test_feature(self, **kwargs) -> None: + created_resource = None + + with self.create_client(**kwargs) as project_client: + try: + # TODO(): exercise the public operation and assert behavior. + created_resource = project_client + assert created_resource is not None + finally: + if created_resource is not None: + # TODO(): delete any service resources created by the test. + pass diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/templates/test-skeleton_async.py b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/templates/test-skeleton_async.py new file mode 100644 index 000000000000..437485ce21b1 --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-author-tests/templates/test-skeleton_async.py @@ -0,0 +1,29 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest + +from devtools_testutils.aio import recorded_by_proxy_async + +from test_base import TestBase, servicePreparer + + +@pytest.mark.skip(reason="TODO(): enable after Test Proxy recordings are added.") +class TestFeatureAsync(TestBase): + + @servicePreparer() + @recorded_by_proxy_async + async def test_feature_async(self, **kwargs) -> None: + created_resource = None + + async with self.create_async_client(**kwargs) as project_client: + try: + # TODO(): exercise the public operation and assert behavior. + created_resource = project_client + assert created_resource is not None + finally: + if created_resource is not None: + # TODO(): await deletion of service resources created by the test. + pass diff --git a/sdk/ai/azure-ai-projects/CHANGELOG.md b/sdk/ai/azure-ai-projects/CHANGELOG.md index 742a6e15a857..1bb95c2433be 100644 --- a/sdk/ai/azure-ai-projects/CHANGELOG.md +++ b/sdk/ai/azure-ai-projects/CHANGELOG.md @@ -2,12 +2,21 @@ ## 2.6.0 (Unreleased) +### Features Added + +* Added the preview Agent and Toolbox tools `WebIQPreviewTool` and `WebIQPreviewToolboxTool`, with new `ToolType.WEB_IQ_PREVIEW` and `ToolboxToolType.WEB_IQ_PREVIEW` enum members. +* Added shell tools for Toolboxes through `ShellToolboxTool` and the new `ToolboxShellEnvironment`, `ToolboxShellContainerAutoEnvironment`, `ToolboxShellContainerReferenceEnvironment`, `ToolboxShellNetworkPolicy`, and `ToolboxShellNetworkPolicyDisabled` models. +* Added the optional `session_configuration` property to `HostedAgentDefinition` and the new `SessionConfiguration` model for configuring the session idle timeout. +* Added the read-only `access_boundaries` property to `ActivityProtocolConfiguration` and the new `ActivityProtocolAccessBoundary` enum. + ### Bugs Fixed * Fixed Responses API instrumentation for `with_raw_response` streaming calls ([GitHub issue 48646](https://github.com/Azure/azure-sdk-for-python/issues/48646)). ### Sample updates +* Added `sample_agent_web_iq.py` under `samples/agents/tools/`, demonstrating a Prompt Agent configured with `WebIQPreviewTool`. +* Added `sample_toolboxes_shell_and_web_iq.py` under `samples/toolboxes/`, demonstrating a Toolbox containing an automatically provisioned shell tool and a WebIQ preview tool. * Added `sample_synthetic_multiturn_evaluation.py`, demonstrating simulation seed generation from an agent followed by multi-turn conversation simulation and evaluation. ## 2.5.0 (2026-08-20) diff --git a/sdk/ai/azure-ai-projects/api.md b/sdk/ai/azure-ai-projects/api.md index a2e744e83a4f..4de08a5faa10 100644 --- a/sdk/ai/azure-ai-projects/api.md +++ b/sdk/ai/azure-ai-projects/api.md @@ -2467,7 +2467,29 @@ namespace azure.ai.projects.models def __init__(self, mapping: Mapping[str, Any]) -> None: ... + class azure.ai.projects.models.ActivityProtocolAccessBoundary(str, Enum, metaclass=CaseInsensitiveEnumMeta): + READ1_ON1_ALLOWLISTED = "read.1on1.allowlisted" + READ1_ON1_DEVELOPERS = "read.1on1.developers" + READ1_ON1_MANAGER = "read.1on1.manager" + READ1_ON1_TENANT = "read.1on1.tenant" + READ_GROUP_ALLOWLISTED = "read.group.allowlisted" + READ_GROUP_DEVELOPERS = "read.group.developers" + READ_GROUP_MANAGER_INVITED = "read.group.manager-invited" + READ_GROUP_MANAGER_PRESENT = "read.group.manager-present" + READ_GROUP_TENANT = "read.group.tenant" + WRITE1_ON1_ALLOWLISTED = "write.1on1.allowlisted" + WRITE1_ON1_DEVELOPERS = "write.1on1.developers" + WRITE1_ON1_MANAGER = "write.1on1.manager" + WRITE1_ON1_TENANT = "write.1on1.tenant" + WRITE_GROUP_ALLOWLISTED = "write.group.allowlisted" + WRITE_GROUP_DEVELOPERS = "write.group.developers" + WRITE_GROUP_MANAGER_INVITED = "write.group.manager-invited" + WRITE_GROUP_MANAGER_PRESENT = "write.group.manager-present" + WRITE_GROUP_TENANT = "write.group.tenant" + + class azure.ai.projects.models.ActivityProtocolConfiguration(_Model): + access_boundaries: Optional[list[Union[str, ActivityProtocolAccessBoundary]]] enable_m365_public_endpoint: Optional[bool] @overload @@ -6101,6 +6123,7 @@ namespace azure.ai.projects.models memory: str protocol_versions: Optional[list[ProtocolVersionRecord]] rai_config: RaiConfig + session_configuration: Optional[SessionConfiguration] telemetry_config: Optional[TelemetryConfig] @overload @@ -6114,6 +6137,7 @@ namespace azure.ai.projects.models memory: str, protocol_versions: Optional[list[ProtocolVersionRecord]] = ..., rai_config: Optional[RaiConfig] = ..., + session_configuration: Optional[SessionConfiguration] = ..., telemetry_config: Optional[TelemetryConfig] = ... ) -> None: ... @@ -8341,6 +8365,20 @@ namespace azure.ai.projects.models MEDIUM = "medium" + class azure.ai.projects.models.SessionConfiguration(_Model): + idle_timeout_seconds: Optional[timedelta] + + @overload + def __init__( + self, + *, + idle_timeout_seconds: Optional[timedelta] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + class azure.ai.projects.models.SessionDirectoryEntry(_Model): is_directory: bool modified_time: datetime @@ -8426,6 +8464,29 @@ namespace azure.ai.projects.models def __init__(self, mapping: Mapping[str, Any]) -> None: ... + class azure.ai.projects.models.ShellToolboxTool(ToolboxTool, discriminator='shell'): + allowed_callers: Optional[list[Union[str, CallableToolAllowedCaller]]] + description: str + environment: ToolboxShellEnvironment + name: str + tool_configs: dict[str, ToolConfig] + type: Literal[ToolboxToolType.SHELL] + + @overload + def __init__( + self, + *, + allowed_callers: Optional[list[Union[str, CallableToolAllowedCaller]]] = ..., + description: Optional[str] = ..., + environment: ToolboxShellEnvironment, + name: Optional[str] = ..., + tool_configs: Optional[dict[str, ToolConfig]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + class azure.ai.projects.models.SimpleQnADataGenerationJobOptions(DataGenerationJobOptions, discriminator='simple_qna'): max_samples: int model_options: DataGenerationModelOptions @@ -9146,6 +9207,7 @@ namespace azure.ai.projects.models SHELL = "shell" TOOLBOX_SEARCH_PREVIEW = "toolbox_search_preview" TOOL_SEARCH = "tool_search" + WEB_IQ_PREVIEW = "web_iq_preview" WEB_SEARCH = "web_search" WEB_SEARCH_PREVIEW = "web_search_preview" WORK_IQ_PREVIEW = "work_iq_preview" @@ -9221,6 +9283,80 @@ namespace azure.ai.projects.models def __init__(self, mapping: Mapping[str, Any]) -> None: ... + class azure.ai.projects.models.ToolboxShellContainerAutoEnvironment(ToolboxShellEnvironment, discriminator='container_auto'): + file_ids: Optional[list[str]] + memory_limit: Optional[Union[str, ContainerMemoryLimit]] + network_policy: Optional[ToolboxShellNetworkPolicy] + skills: Optional[list[ContainerSkill]] + type: Literal["container_auto"] + + @overload + def __init__( + self, + *, + file_ids: Optional[list[str]] = ..., + memory_limit: Optional[Union[str, ContainerMemoryLimit]] = ..., + network_policy: Optional[ToolboxShellNetworkPolicy] = ..., + skills: Optional[list[ContainerSkill]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.projects.models.ToolboxShellContainerReferenceEnvironment(ToolboxShellEnvironment, discriminator='container_reference'): + container_id: str + type: Literal["container_reference"] + + @overload + def __init__( + self, + *, + container_id: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.projects.models.ToolboxShellEnvironment(_Model): + type: str + + @overload + def __init__( + self, + *, + type: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.projects.models.ToolboxShellNetworkPolicy(_Model): + type: str + + @overload + def __init__( + self, + *, + type: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.projects.models.ToolboxShellNetworkPolicyDisabled(ToolboxShellNetworkPolicy, discriminator='disabled'): + type: Literal["disabled"] + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + class azure.ai.projects.models.ToolboxSkill(_Model): type: str @@ -9283,8 +9419,10 @@ namespace azure.ai.projects.models MCP = "mcp" OPENAPI = "openapi" REMINDER_PREVIEW = "reminder_preview" + SHELL = "shell" TOOLBOX_SEARCH = "toolbox_search" TOOLBOX_SEARCH_PREVIEW = "toolbox_search_preview" + WEB_IQ_PREVIEW = "web_iq_preview" WEB_SEARCH = "web_search" WORK_IQ_PREVIEW = "work_iq_preview" @@ -9560,6 +9698,50 @@ namespace azure.ai.projects.models FIXED_RATIO = "FixedRatio" + class azure.ai.projects.models.WebIQPreviewTool(Tool, discriminator='web_iq_preview'): + project_connection_id: str + require_approval: Optional[Union[MCPToolRequireApproval, str]] + server_label: Optional[str] + type: Literal[ToolType.WEB_IQ_PREVIEW] + + @overload + def __init__( + self, + *, + project_connection_id: str, + require_approval: Optional[Union[MCPToolRequireApproval, str]] = ..., + server_label: Optional[str] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.projects.models.WebIQPreviewToolboxTool(ToolboxTool, discriminator='web_iq_preview'): + description: str + name: str + project_connection_id: str + require_approval: Optional[Union[MCPToolRequireApproval, str]] + server_label: Optional[str] + tool_configs: dict[str, ToolConfig] + type: Literal[ToolboxToolType.WEB_IQ_PREVIEW] + + @overload + def __init__( + self, + *, + description: Optional[str] = ..., + name: Optional[str] = ..., + project_connection_id: str, + require_approval: Optional[Union[MCPToolRequireApproval, str]] = ..., + server_label: Optional[str] = ..., + tool_configs: Optional[dict[str, ToolConfig]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + class azure.ai.projects.models.WebSearchApproximateLocation(_Model): city: Optional[str] country: Optional[str] diff --git a/sdk/ai/azure-ai-projects/api.metadata.yml b/sdk/ai/azure-ai-projects/api.metadata.yml index 5923ed96d577..b33c38195162 100644 --- a/sdk/ai/azure-ai-projects/api.metadata.yml +++ b/sdk/ai/azure-ai-projects/api.metadata.yml @@ -1,3 +1,3 @@ -apiMdSha256: 55a629feb8b0501f399dfa3740cf44231ebfef50128f7fb7c85ce0e60bbefe96 -parserVersion: 0.3.28 -pythonVersion: 3.14.3 +apiMdSha256: 059f1e3add616fc0439638cb792c7b1ebee0e59debeedd62f3cc2a00ebcd96aa +parserVersion: 0.3.31 +pythonVersion: 3.12.13 diff --git a/sdk/ai/azure-ai-projects/apiview-properties.json b/sdk/ai/azure-ai-projects/apiview-properties.json index 3ce7269f875e..6c786b5e9154 100644 --- a/sdk/ai/azure-ai-projects/apiview-properties.json +++ b/sdk/ai/azure-ai-projects/apiview-properties.json @@ -294,11 +294,13 @@ "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.SessionConfiguration": "Azure.AI.Projects.SessionConfiguration", "azure.ai.projects.models.SessionDirectoryEntry": "Azure.AI.Projects.SessionDirectoryEntry", "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.ShellToolboxTool": "Azure.AI.Projects.ShellToolboxTool", "azure.ai.projects.models.SimpleQnADataGenerationJobOptions": "Azure.AI.Projects.SimpleQnADataGenerationJobOptions", "azure.ai.projects.models.SimulationSeedDataGenerationJobOptions": "Azure.AI.Projects.SimulationSeedDataGenerationJobOptions", "azure.ai.projects.models.SkillDetails": "Azure.AI.Projects.Skill", @@ -322,6 +324,11 @@ "azure.ai.projects.models.ToolboxObject": "Azure.AI.Projects.ToolboxObject", "azure.ai.projects.models.ToolboxPolicies": "Azure.AI.Projects.ToolboxPolicies", "azure.ai.projects.models.ToolboxSearchPreviewToolboxTool": "Azure.AI.Projects.ToolboxSearchPreviewToolboxTool", + "azure.ai.projects.models.ToolboxShellEnvironment": "Azure.AI.Projects.ToolboxShellEnvironment", + "azure.ai.projects.models.ToolboxShellContainerAutoEnvironment": "Azure.AI.Projects.ToolboxShellContainerAutoEnvironment", + "azure.ai.projects.models.ToolboxShellContainerReferenceEnvironment": "Azure.AI.Projects.ToolboxShellContainerReferenceEnvironment", + "azure.ai.projects.models.ToolboxShellNetworkPolicy": "Azure.AI.Projects.ToolboxShellNetworkPolicy", + "azure.ai.projects.models.ToolboxShellNetworkPolicyDisabled": "Azure.AI.Projects.ToolboxShellNetworkPolicyDisabled", "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", @@ -352,6 +359,8 @@ "azure.ai.projects.models.VersionIndicator": "Azure.AI.Projects.VersionIndicator", "azure.ai.projects.models.VersionRefIndicator": "Azure.AI.Projects.VersionRefIndicator", "azure.ai.projects.models.VersionSelector": "Azure.AI.Projects.VersionSelector", + "azure.ai.projects.models.WebIQPreviewTool": "Azure.AI.Projects.WebIQPreviewTool", + "azure.ai.projects.models.WebIQPreviewToolboxTool": "Azure.AI.Projects.WebIQPreviewToolboxTool", "azure.ai.projects.models.WebSearchApproximateLocation": "OpenAI.WebSearchApproximateLocation", "azure.ai.projects.models.WebSearchConfiguration": "Azure.AI.Projects.WebSearchConfiguration", "azure.ai.projects.models.WebSearchPreviewTool": "OpenAI.WebSearchPreviewTool", @@ -445,6 +454,7 @@ "azure.ai.projects.models.AgentIdentityStatus": "Azure.AI.Projects.AgentIdentityStatus", "azure.ai.projects.models.AgentBlueprintReferenceType": "Azure.AI.Projects.AgentBlueprintReferenceType", "azure.ai.projects.models.VersionSelectorType": "Azure.AI.Projects.VersionSelectorType", + "azure.ai.projects.models.ActivityProtocolAccessBoundary": "Azure.AI.Projects.ActivityProtocolAccessBoundary", "azure.ai.projects.models.AgentEndpointAuthorizationSchemeType": "Azure.AI.Projects.AgentEndpointAuthorizationSchemeType", "azure.ai.projects.models.VersionIndicatorType": "Azure.AI.Projects.VersionIndicatorType", "azure.ai.projects.models.AgentSessionStatus": "Azure.AI.Projects.AgentSessionStatus", @@ -557,5 +567,5 @@ "azure.ai.projects.operations.ToolboxesOperations.delete_version": "Azure.AI.Projects.Toolboxes.deleteToolboxVersion", "azure.ai.projects.aio.operations.ToolboxesOperations.delete_version": "Azure.AI.Projects.Toolboxes.deleteToolboxVersion" }, - "CrossLanguageVersion": "f23cc7b21030" + "CrossLanguageVersion": "1cb0522717b7" } \ No newline at end of file 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 1261682860e9..5efcd3f90a71 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 @@ -301,11 +301,13 @@ ScheduleRoutineTrigger, ScheduleRun, ScheduleTask, + SessionConfiguration, SessionDirectoryEntry, SessionFileWriteResult, SessionLogEvent, SharepointGroundingToolParameters, SharepointPreviewTool, + ShellToolboxTool, SimpleQnADataGenerationJobOptions, SimulationSeedDataGenerationJobOptions, SkillDetails, @@ -350,6 +352,11 @@ ToolboxObject, ToolboxPolicies, ToolboxSearchPreviewToolboxTool, + ToolboxShellContainerAutoEnvironment, + ToolboxShellContainerReferenceEnvironment, + ToolboxShellEnvironment, + ToolboxShellNetworkPolicy, + ToolboxShellNetworkPolicyDisabled, ToolboxSkill, ToolboxSkillReference, ToolboxTool, @@ -365,6 +372,8 @@ VersionRefIndicator, VersionSelectionRule, VersionSelector, + WebIQPreviewTool, + WebIQPreviewToolboxTool, WebSearchApproximateLocation, WebSearchConfiguration, WebSearchPreviewTool, @@ -379,6 +388,7 @@ from ._enums import ( # type: ignore A2AProtocolVersion, + ActivityProtocolAccessBoundary, AgentBlueprintReferenceType, AgentEndpointAuthorizationSchemeType, AgentEndpointProtocol, @@ -767,11 +777,13 @@ "ScheduleRoutineTrigger", "ScheduleRun", "ScheduleTask", + "SessionConfiguration", "SessionDirectoryEntry", "SessionFileWriteResult", "SessionLogEvent", "SharepointGroundingToolParameters", "SharepointPreviewTool", + "ShellToolboxTool", "SimpleQnADataGenerationJobOptions", "SimulationSeedDataGenerationJobOptions", "SkillDetails", @@ -816,6 +828,11 @@ "ToolboxObject", "ToolboxPolicies", "ToolboxSearchPreviewToolboxTool", + "ToolboxShellContainerAutoEnvironment", + "ToolboxShellContainerReferenceEnvironment", + "ToolboxShellEnvironment", + "ToolboxShellNetworkPolicy", + "ToolboxShellNetworkPolicyDisabled", "ToolboxSkill", "ToolboxSkillReference", "ToolboxTool", @@ -831,6 +848,8 @@ "VersionRefIndicator", "VersionSelectionRule", "VersionSelector", + "WebIQPreviewTool", + "WebIQPreviewToolboxTool", "WebSearchApproximateLocation", "WebSearchConfiguration", "WebSearchPreviewTool", @@ -842,6 +861,7 @@ "WorkIQPreviewToolboxTool", "WorkflowAgentDefinition", "A2AProtocolVersion", + "ActivityProtocolAccessBoundary", "AgentBlueprintReferenceType", "AgentEndpointAuthorizationSchemeType", "AgentEndpointProtocol", 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 8b3f138d31bb..b49102e9d53b 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 @@ -47,6 +47,8 @@ class _FoundryFeaturesOptInKeys(str, Enum, metaclass=CaseInsensitiveEnumMeta): """MODELS_V1_PREVIEW.""" AGENTS_OPTIMIZATION_V2_PREVIEW = "AgentsOptimization=V2Preview" """AGENTS_OPTIMIZATION_V2_PREVIEW.""" + MODEL_ROUTER_CONTROLS_V1_PREVIEW = "ModelRouterControls=V1Preview" + """MODEL_ROUTER_CONTROLS_V1_PREVIEW.""" class A2AProtocolVersion(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -56,6 +58,47 @@ class A2AProtocolVersion(str, Enum, metaclass=CaseInsensitiveEnumMeta): """A2A protocol version 1.0.""" +class ActivityProtocolAccessBoundary(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """An access boundary for the activity protocol.""" + + READ1_ON1_DEVELOPERS = "read.1on1.developers" + """Allows read access to one-on-one developer conversations.""" + READ1_ON1_MANAGER = "read.1on1.manager" + """Allows read access to one-on-one manager conversations.""" + READ1_ON1_ALLOWLISTED = "read.1on1.allowlisted" + """Allows read access to allowlisted one-on-one conversations.""" + READ1_ON1_TENANT = "read.1on1.tenant" + """Allows read access to tenant-wide one-on-one conversations.""" + WRITE1_ON1_DEVELOPERS = "write.1on1.developers" + """Allows write access to one-on-one developer conversations.""" + WRITE1_ON1_MANAGER = "write.1on1.manager" + """Allows write access to one-on-one manager conversations.""" + WRITE1_ON1_ALLOWLISTED = "write.1on1.allowlisted" + """Allows write access to allowlisted one-on-one conversations.""" + WRITE1_ON1_TENANT = "write.1on1.tenant" + """Allows write access to tenant-wide one-on-one conversations.""" + READ_GROUP_DEVELOPERS = "read.group.developers" + """Allows read access to developer group conversations.""" + READ_GROUP_ALLOWLISTED = "read.group.allowlisted" + """Allows read access to allowlisted group conversations.""" + READ_GROUP_MANAGER_INVITED = "read.group.manager-invited" + """Allows read access to group conversations where a manager is invited.""" + READ_GROUP_MANAGER_PRESENT = "read.group.manager-present" + """Allows read access to group conversations where a manager is present.""" + READ_GROUP_TENANT = "read.group.tenant" + """Allows read access to tenant-wide group conversations.""" + WRITE_GROUP_DEVELOPERS = "write.group.developers" + """Allows write access to developer group conversations.""" + WRITE_GROUP_ALLOWLISTED = "write.group.allowlisted" + """Allows write access to allowlisted group conversations.""" + WRITE_GROUP_MANAGER_INVITED = "write.group.manager-invited" + """Allows write access to group conversations where a manager is invited.""" + WRITE_GROUP_MANAGER_PRESENT = "write.group.manager-present" + """Allows write access to group conversations where a manager is present.""" + WRITE_GROUP_TENANT = "write.group.tenant" + """Allows write access to tenant-wide group conversations.""" + + class AgentBlueprintReferenceType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Type of AgentBlueprintReferenceType.""" @@ -1175,14 +1218,14 @@ class ToolboxToolType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """FILE_SEARCH.""" WEB_SEARCH = "web_search" """WEB_SEARCH.""" + SHELL = "shell" + """SHELL.""" MCP = "mcp" """MCP.""" AZURE_AI_SEARCH = "azure_ai_search" """AZURE_AI_SEARCH.""" OPENAPI = "openapi" """OPENAPI.""" - A2_A = "a2a" - """A2_A.""" A2A_PREVIEW = "a2a_preview" """A2A_PREVIEW.""" BROWSER_AUTOMATION_PREVIEW = "browser_automation_preview" @@ -1197,6 +1240,10 @@ class ToolboxToolType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """TOOLBOX_SEARCH.""" TOOLBOX_SEARCH_PREVIEW = "toolbox_search_preview" """TOOLBOX_SEARCH_PREVIEW.""" + WEB_IQ_PREVIEW = "web_iq_preview" + """WEB_IQ_PREVIEW.""" + A2_A = "a2a" + """A2_A.""" class ToolChoiceParamType(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -1296,6 +1343,8 @@ class ToolType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """FABRIC_IQ_PREVIEW.""" TOOLBOX_SEARCH_PREVIEW = "toolbox_search_preview" """TOOLBOX_SEARCH_PREVIEW.""" + WEB_IQ_PREVIEW = "web_iq_preview" + """WEB_IQ_PREVIEW.""" A2_A = "a2a" """A2_A.""" AZURE_AI_SEARCH = "azure_ai_search" 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 1fbcd59d0c91..aaf05b5c66b2 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 @@ -162,7 +162,7 @@ class Tool(_Model): # pylint: disable=docstring-keyword-should-match-keyword-on CustomToolParam, MicrosoftFabricPreviewTool, FabricIQPreviewTool, FileSearchTool, FunctionTool, ImageGenTool, LocalShellToolParam, MCPTool, MemorySearchPreviewTool, NamespaceToolParam, OpenApiTool, ProgrammaticToolCallingParam, SharepointPreviewTool, FunctionShellToolParam, - ToolSearchToolParam, WebSearchTool, WebSearchPreviewTool, WorkIQPreviewTool + ToolSearchToolParam, WebIQPreviewTool, WebSearchTool, WebSearchPreviewTool, WorkIQPreviewTool :ivar type: Required. Known values are: "function", "file_search", "computer", "computer_use_preview", "web_search", "mcp", "code_interpreter", "programmatic_tool_calling", @@ -170,8 +170,8 @@ class Tool(_Model): # pylint: disable=docstring-keyword-should-match-keyword-on "web_search_preview", "apply_patch", "a2a_preview", "bing_custom_search_preview", "browser_automation_preview", "fabric_dataagent_preview", "sharepoint_grounding_preview", "memory_search_preview", "work_iq_preview", "fabric_iq_preview", "toolbox_search_preview", - "a2a", "azure_ai_search", "azure_function", "bing_grounding", "capture_structured_outputs", and - "openapi". + "web_iq_preview", "a2a", "azure_ai_search", "azure_function", "bing_grounding", + "capture_structured_outputs", and "openapi". :vartype type: str or ~azure.ai.projects.models.ToolType """ @@ -183,8 +183,9 @@ class Tool(_Model): # pylint: disable=docstring-keyword-should-match-keyword-on \"namespace\", \"tool_search\", \"web_search_preview\", \"apply_patch\", \"a2a_preview\", \"bing_custom_search_preview\", \"browser_automation_preview\", \"fabric_dataagent_preview\", \"sharepoint_grounding_preview\", \"memory_search_preview\", \"work_iq_preview\", - \"fabric_iq_preview\", \"toolbox_search_preview\", \"a2a\", \"azure_ai_search\", - \"azure_function\", \"bing_grounding\", \"capture_structured_outputs\", and \"openapi\".""" + \"fabric_iq_preview\", \"toolbox_search_preview\", \"web_iq_preview\", \"a2a\", + \"azure_ai_search\", \"azure_function\", \"bing_grounding\", \"capture_structured_outputs\", + and \"openapi\".""" @overload def __init__( @@ -269,13 +270,13 @@ class ToolboxTool(_Model): # pylint: disable=docstring-keyword-should-match-key A2AToolboxTool, A2APreviewToolboxTool, AzureAISearchToolboxTool, BrowserAutomationPreviewToolboxTool, CodeInterpreterToolboxTool, FabricIQPreviewToolboxTool, FileSearchToolboxTool, MCPToolboxTool, OpenApiToolboxTool, ReminderPreviewToolboxTool, - ToolSearchToolboxTool, ToolboxSearchPreviewToolboxTool, WebSearchToolboxTool, - WorkIQPreviewToolboxTool + ShellToolboxTool, ToolSearchToolboxTool, ToolboxSearchPreviewToolboxTool, + WebIQPreviewToolboxTool, WebSearchToolboxTool, WorkIQPreviewToolboxTool :ivar type: The type of tool. Required. Known values are: "code_interpreter", "file_search", - "web_search", "mcp", "azure_ai_search", "openapi", "a2a", "a2a_preview", + "web_search", "shell", "mcp", "azure_ai_search", "openapi", "a2a_preview", "browser_automation_preview", "reminder_preview", "work_iq_preview", "fabric_iq_preview", - "toolbox_search", and "toolbox_search_preview". + "toolbox_search", "toolbox_search_preview", "web_iq_preview", and "a2a". :vartype type: str or ~azure.ai.projects.models.ToolboxToolType :ivar name: Optional user-defined name for this tool or configuration. :vartype name: str @@ -290,9 +291,10 @@ class ToolboxTool(_Model): # pylint: disable=docstring-keyword-should-match-key __mapping__: dict[str, _Model] = {} type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) """The type of tool. Required. Known values are: \"code_interpreter\", \"file_search\", - \"web_search\", \"mcp\", \"azure_ai_search\", \"openapi\", \"a2a\", \"a2a_preview\", + \"web_search\", \"shell\", \"mcp\", \"azure_ai_search\", \"openapi\", \"a2a_preview\", \"browser_automation_preview\", \"reminder_preview\", \"work_iq_preview\", - \"fabric_iq_preview\", \"toolbox_search\", and \"toolbox_search_preview\".""" + \"fabric_iq_preview\", \"toolbox_search\", \"toolbox_search_preview\", \"web_iq_preview\", and + \"a2a\".""" 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"]) @@ -547,10 +549,17 @@ class ActivityProtocolConfiguration(_Model): # pylint: disable=docstring-keywor :ivar enable_m365_public_endpoint: Whether to enable the M365 public endpoint for the activity protocol. :vartype enable_m365_public_endpoint: bool + :ivar access_boundaries: The access boundaries for the activity protocol. + :vartype access_boundaries: list[str or + ~azure.ai.projects.models.ActivityProtocolAccessBoundary] """ enable_m365_public_endpoint: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Whether to enable the M365 public endpoint for the activity protocol.""" + access_boundaries: Optional[list[Union[str, "_models.ActivityProtocolAccessBoundary"]]] = rest_field( + visibility=["read"] + ) + """The access boundaries for the activity protocol.""" @overload def __init__( @@ -8963,6 +8972,9 @@ class HostedAgentDefinition( :ivar telemetry_config: Optional customer-supplied telemetry configuration for exporting container logs, traces, and metrics. :vartype telemetry_config: ~azure.ai.projects.models.TelemetryConfig + :ivar session_configuration: Optional session defaults (for example, the idle timeout) applied + to sessions created for this agent version. + :vartype session_configuration: ~azure.ai.projects.models.SessionConfiguration """ kind: Literal[AgentKind.HOSTED] = rest_discriminator(name="kind", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -8994,6 +9006,11 @@ class HostedAgentDefinition( ) """Optional customer-supplied telemetry configuration for exporting container logs, traces, and metrics.""" + session_configuration: Optional["_models.SessionConfiguration"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Optional session defaults (for example, the idle timeout) applied to sessions created for this + agent version.""" @overload def __init__( @@ -9007,6 +9024,7 @@ def __init__( protocol_versions: Optional[list["_models.ProtocolVersionRecord"]] = None, code_configuration: Optional["_models.CodeConfiguration"] = None, telemetry_config: Optional["_models.TelemetryConfig"] = None, + session_configuration: Optional["_models.SessionConfiguration"] = None, ) -> None: ... @overload @@ -13813,6 +13831,39 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class SessionConfiguration(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """Session defaults applied to sessions created for a hosted agent version. + + :ivar idle_timeout_seconds: The idle duration, in seconds, before a session's sandbox is + suspended. Optional — when unset, the server default of 900 seconds is used. Must be between + 300 and 3600 seconds (inclusive). + :vartype idle_timeout_seconds: ~datetime.timedelta + """ + + idle_timeout_seconds: Optional[datetime.timedelta] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="duration-seconds-int" + ) + """The idle duration, in seconds, before a session's sandbox is suspended. Optional — when unset, + the server default of 900 seconds is used. Must be between 300 and 3600 seconds (inclusive).""" + + @overload + def __init__( + self, + *, + idle_timeout_seconds: Optional[datetime.timedelta] = 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 SessionDirectoryEntry(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only """A single entry in a directory listing. @@ -14022,6 +14073,63 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = ToolType.SHAREPOINT_GROUNDING_PREVIEW # type: ignore +class ShellToolboxTool( + ToolboxTool, discriminator="shell" +): # pylint: disable=docstring-keyword-should-match-keyword-only + """A shell tool stored in a toolbox. This model is additive to toolbox configuration and does not + modify the OpenAI tool contract or existing toolbox tool definitions. + + :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 type: The type of the tool. Always ``shell``. Required. SHELL. + :vartype type: str or ~azure.ai.projects.models.SHELL + :ivar allowed_callers: + :vartype allowed_callers: list[str or ~azure.ai.projects.models.CallableToolAllowedCaller] + :ivar environment: The environment in which shell commands are executed. Specify an + automatically provisioned container or an existing container. Required. + :vartype environment: ~azure.ai.projects.models.ToolboxShellEnvironment + """ + + type: Literal[ToolboxToolType.SHELL] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the tool. Always ``shell``. Required. SHELL.""" + allowed_callers: Optional[list[Union[str, "_models.CallableToolAllowedCaller"]]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + environment: "_models.ToolboxShellEnvironment" = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The environment in which shell commands are executed. Specify an automatically provisioned + container or an existing container. Required.""" + + @overload + def __init__( + self, + *, + environment: "_models.ToolboxShellEnvironment", + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, + allowed_callers: Optional[list[Union[str, "_models.CallableToolAllowedCaller"]]] = 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 = ToolboxToolType.SHELL # type: ignore + + class SimpleQnADataGenerationJobOptions( DataGenerationJobOptions, discriminator="simple_qna" ): # pylint: disable=docstring-keyword-should-match-keyword-only @@ -14974,6 +15082,197 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = ToolboxToolType.TOOLBOX_SEARCH_PREVIEW # type: ignore +class ToolboxShellEnvironment(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """An execution environment for a shell tool stored in a toolbox. This environment model is scoped + to toolbox configuration and does not modify the OpenAI shell environment contract. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + ToolboxShellContainerAutoEnvironment, ToolboxShellContainerReferenceEnvironment + + :ivar type: The type of the shell execution environment. Required. Default value is None. + :vartype type: str + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """The type of the shell execution environment. Required. Default value is None.""" + + @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 ToolboxShellContainerAutoEnvironment( + ToolboxShellEnvironment, discriminator="container_auto" +): # pylint: disable=docstring-keyword-should-match-keyword-only + """An automatically provisioned container environment for a shell tool stored in a toolbox. + + :ivar type: The type of the shell execution environment. Always ``container_auto``. Required. + Default value is "container_auto". + :vartype type: str + :ivar file_ids: An optional list of uploaded files to make available to your code. + :vartype file_ids: list[str] + :ivar memory_limit: Known values are: "1g", "4g", "16g", and "64g". + :vartype memory_limit: str or ~azure.ai.projects.models.ContainerMemoryLimit + :ivar skills: An optional list of skills referenced by id or inline data. + :vartype skills: list[~azure.ai.projects.models.ContainerSkill] + :ivar network_policy: The network access policy for the container. When omitted, the service + defaults to disabled outbound network access. + :vartype network_policy: ~azure.ai.projects.models.ToolboxShellNetworkPolicy + """ + + type: Literal["container_auto"] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the shell execution environment. Always ``container_auto``. Required. Default value + is \"container_auto\".""" + file_ids: Optional[list[str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """An optional list of uploaded files to make available to your code.""" + memory_limit: Optional[Union[str, "_models.ContainerMemoryLimit"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Known values are: \"1g\", \"4g\", \"16g\", and \"64g\".""" + skills: Optional[list["_models.ContainerSkill"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """An optional list of skills referenced by id or inline data.""" + network_policy: Optional["_models.ToolboxShellNetworkPolicy"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The network access policy for the container. When omitted, the service defaults to disabled + outbound network access.""" + + @overload + def __init__( + self, + *, + file_ids: Optional[list[str]] = None, + memory_limit: Optional[Union[str, "_models.ContainerMemoryLimit"]] = None, + skills: Optional[list["_models.ContainerSkill"]] = None, + network_policy: Optional["_models.ToolboxShellNetworkPolicy"] = 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 = "container_auto" # type: ignore + + +class ToolboxShellContainerReferenceEnvironment( + ToolboxShellEnvironment, discriminator="container_reference" +): # pylint: disable=name-too-long,docstring-keyword-should-match-keyword-only + """An existing container environment for a shell tool stored in a toolbox. + + :ivar type: The type of the shell execution environment. Always ``container_reference``. + Required. Default value is "container_reference". + :vartype type: str + :ivar container_id: The ID of the referenced container. Required. + :vartype container_id: str + """ + + type: Literal["container_reference"] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the shell execution environment. Always ``container_reference``. Required. Default + value is \"container_reference\".""" + container_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The ID of the referenced container. Required.""" + + @overload + def __init__( + self, + *, + container_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) + self.type = "container_reference" # type: ignore + + +class ToolboxShellNetworkPolicy(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """Network access policy for an automatically provisioned toolbox shell container. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + ToolboxShellNetworkPolicyDisabled + + :ivar type: The type of network access policy. Required. Default value is None. + :vartype type: str + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """The type of network access policy. Required. Default value is None.""" + + @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 ToolboxShellNetworkPolicyDisabled(ToolboxShellNetworkPolicy, discriminator="disabled"): + """A network policy that disables outbound access from a toolbox shell container. + + :ivar type: The type of network access policy. Always ``disabled``. Required. Default value is + "disabled". + :vartype type: str + """ + + type: Literal["disabled"] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of network access policy. Always ``disabled``. Required. Default value is + \"disabled\".""" + + @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 = "disabled" # type: ignore + + class ToolboxSkill(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only """A skill source included in a toolbox. @@ -16168,6 +16467,121 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class WebIQPreviewTool( + Tool, discriminator="web_iq_preview" +): # pylint: disable=docstring-keyword-should-match-keyword-only + """A WebIQ server-side tool. + + :ivar type: The object type, which is always 'web_iq_preview'. Required. WEB_IQ_PREVIEW. + :vartype type: str or ~azure.ai.projects.models.WEB_IQ_PREVIEW + :ivar project_connection_id: The ID of the WebIQ project connection. Required. + :vartype project_connection_id: str + :ivar server_label: The label of the WebIQ MCP server to connect to. When omitted, the service + defaults to connection name extracted from project_connection_id. + :vartype server_label: str + :ivar require_approval: Whether the agent requires approval before executing actions. When + omitted, the service defaults to "always". Is either a MCPToolRequireApproval type or a str + type. + :vartype require_approval: ~azure.ai.projects.models.MCPToolRequireApproval or str + """ + + type: Literal[ToolType.WEB_IQ_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The object type, which is always 'web_iq_preview'. Required. WEB_IQ_PREVIEW.""" + project_connection_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The ID of the WebIQ project connection. Required.""" + server_label: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The label of the WebIQ MCP server to connect to. When omitted, the service defaults to + connection name extracted from project_connection_id.""" + require_approval: Optional[Union["_models.MCPToolRequireApproval", str]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Whether the agent requires approval before executing actions. When omitted, the service + defaults to \"always\". Is either a MCPToolRequireApproval type or a str type.""" + + @overload + def __init__( + self, + *, + project_connection_id: str, + server_label: Optional[str] = None, + require_approval: Optional[Union["_models.MCPToolRequireApproval", 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 = ToolType.WEB_IQ_PREVIEW # type: ignore + + +class WebIQPreviewToolboxTool( + ToolboxTool, discriminator="web_iq_preview" +): # pylint: disable=docstring-keyword-should-match-keyword-only + """A WebIQ tool stored in a toolbox. + + :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 type: Required. WEB_IQ_PREVIEW. + :vartype type: str or ~azure.ai.projects.models.WEB_IQ_PREVIEW + :ivar project_connection_id: The ID of the WebIQ project connection. Required. + :vartype project_connection_id: str + :ivar server_label: The label of the WebIQ MCP server to connect to. When omitted, the service + defaults to connection name extracted from project_connection_id. + :vartype server_label: str + :ivar require_approval: Whether the agent requires approval before executing actions. When + omitted, the service defaults to "always". Is either a MCPToolRequireApproval type or a str + type. + :vartype require_approval: ~azure.ai.projects.models.MCPToolRequireApproval or str + """ + + type: Literal[ToolboxToolType.WEB_IQ_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. WEB_IQ_PREVIEW.""" + project_connection_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The ID of the WebIQ project connection. Required.""" + server_label: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The label of the WebIQ MCP server to connect to. When omitted, the service defaults to + connection name extracted from project_connection_id.""" + require_approval: Optional[Union["_models.MCPToolRequireApproval", str]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Whether the agent requires approval before executing actions. When omitted, the service + defaults to \"always\". Is either a MCPToolRequireApproval type or a str type.""" + + @overload + def __init__( + self, + *, + project_connection_id: str, + name: Optional[str] = None, + description: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, + server_label: Optional[str] = None, + require_approval: Optional[Union["_models.MCPToolRequireApproval", 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 = ToolboxToolType.WEB_IQ_PREVIEW # type: ignore + + class WebSearchApproximateLocation(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only """Web search approximate location. 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 81717c62e046..6b279e3d1c16 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 @@ -57,6 +57,7 @@ _AgentDefinitionOptInKeys.EXTERNAL_AGENTS_V1_PREVIEW.value, _AgentDefinitionOptInKeys.DRAFT_AGENTS_V1_PREVIEW.value, _FoundryFeaturesOptInKeys.AGENTS_OPTIMIZATION_V2_PREVIEW.value, + _FoundryFeaturesOptInKeys.MODEL_ROUTER_CONTROLS_V1_PREVIEW.value, ] ) diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_iq.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_iq.py new file mode 100644 index 000000000000..63dfecc7b801 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_iq.py @@ -0,0 +1,71 @@ +# 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 + WebIQ preview tool with a synchronous client. + +USAGE: + python sample_agent_web_iq.py + + Before running the sample: + + pip install "azure-ai-projects>=2.6.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) FOUNDRY_AGENT_NAME - Optional. The name of the AI agent. If not set, defaults to "MyAgent". + 4) WEB_IQ_PROJECT_CONNECTION_ID - The fully-qualified resource ID of the WebIQ project connection. + 5) WEB_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, WebIQPreviewTool + +from util import create_version_with_endpoint + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +agent_name = os.environ.get("FOUNDRY_AGENT_NAME") or "MyAgent" + +tool_payload = WebIQPreviewTool( + project_connection_id=os.environ["WEB_IQ_PROJECT_CONNECTION_ID"], +) + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + create_version_with_endpoint( + project_client=project_client, + agent_name=agent_name, + definition=PromptAgentDefinition( + model=os.environ["FOUNDRY_MODEL_NAME"], + instructions="Use the available WebIQ tools to answer questions and perform tasks.", + tools=[tool_payload], + ), + ), + project_client.get_openai_client(agent_name=agent_name) as openai_client, +): + user_input = os.environ.get("WEB_IQ_USER_INPUT") or input("Enter your question:\n") + + response = openai_client.responses.create( + input=user_input, + ) + + print(f"Agent response: {response.output_text}") + + # The helper restores the endpoint and deletes the temporary version automatically. diff --git a/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_iq_async.py b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_iq_async.py new file mode 100644 index 000000000000..4a97ad7ad59d --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/agents/tools/sample_agent_web_iq_async.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 run a Prompt Agent that uses the + WebIQ preview tool with an asynchronous client. + +USAGE: + python sample_agent_web_iq_async.py + + Before running the sample: + + pip install "azure-ai-projects>=2.6.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) FOUNDRY_AGENT_NAME - Optional. The name of the AI agent. If not set, defaults to "MyAgent". + 4) WEB_IQ_PROJECT_CONNECTION_ID - The fully-qualified resource ID of the WebIQ project connection. + 5) WEB_IQ_USER_INPUT - The natural-language question to send to the agent. +""" + +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 PromptAgentDefinition, WebIQPreviewTool + +from util import create_version_with_endpoint_async + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +agent_name = os.environ.get("FOUNDRY_AGENT_NAME") or "MyAgent" + + +async def main() -> None: + tool_payload = WebIQPreviewTool( + project_connection_id=os.environ["WEB_IQ_PROJECT_CONNECTION_ID"], + ) + + async with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + create_version_with_endpoint_async( + project_client=project_client, + agent_name=agent_name, + definition=PromptAgentDefinition( + model=os.environ["FOUNDRY_MODEL_NAME"], + instructions="Use the available WebIQ tools to answer questions and perform tasks.", + tools=[tool_payload], + ), + ), + project_client.get_openai_client(agent_name=agent_name) as openai_client, + ): + agent = await project_client.agents.get(agent_name=agent_name) + print(f"Agent created (id: {agent.id}, name: {agent.name}, version: {agent.versions.latest.version})") + + user_input = os.environ.get("WEB_IQ_USER_INPUT") or input("Enter your question:\n") + + response = await openai_client.responses.create( + input=user_input, + ) + + print(f"Agent response: {response.output_text}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/sdk/ai/azure-ai-projects/samples/toolboxes/sample_toolboxes_shell_and_web_iq.py b/sdk/ai/azure-ai-projects/samples/toolboxes/sample_toolboxes_shell_and_web_iq.py new file mode 100644 index 000000000000..b0337d59cf3f --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/toolboxes/sample_toolboxes_shell_and_web_iq.py @@ -0,0 +1,86 @@ +# 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 containing a shell tool + backed by an automatically provisioned container and a WebIQ preview tool + using the synchronous AIProjectClient. + +USAGE: + python sample_toolboxes_shell_and_web_iq.py + + Before running the sample: + + pip install "azure-ai-projects>=2.6.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) WEB_IQ_PROJECT_CONNECTION_ID - The fully-qualified resource ID of the WebIQ project connection. +""" + +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 ( + ShellToolboxTool, + ToolboxShellContainerAutoEnvironment, + ToolboxShellNetworkPolicyDisabled, + WebIQPreviewToolboxTool, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +toolbox_name = "toolbox_with_shell_and_web_iq" + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, +): + created = None + try: + try: + project_client.toolboxes.delete(name=toolbox_name) + except ResourceNotFoundError: + pass + + created = project_client.toolboxes.create_version( + name=toolbox_name, + description="Toolbox with shell and WebIQ tools.", + tools=[ + ShellToolboxTool( + name="shell", + description="Run shell commands in an automatically provisioned container.", + environment=ToolboxShellContainerAutoEnvironment( + memory_limit="4g", + network_policy=ToolboxShellNetworkPolicyDisabled(), + ), + ), + WebIQPreviewToolboxTool( + name="web_iq", + description="Use WebIQ through a project connection.", + project_connection_id=os.environ["WEB_IQ_PROJECT_CONNECTION_ID"], + server_label="web-iq", + require_approval="always", + ), + ], + ) + print(f"Created toolbox `{created.name}` version {created.version}") + + fetched = project_client.toolboxes.get_version(name=toolbox_name, version=created.version) + for tool in fetched.tools or []: + print(f"Tool `{tool.name}` has type `{tool.type}`") + finally: + if created is not None: + project_client.toolboxes.delete(name=toolbox_name) + print("Toolbox deleted") diff --git a/sdk/ai/azure-ai-projects/samples/toolboxes/sample_toolboxes_shell_and_web_iq_async.py b/sdk/ai/azure-ai-projects/samples/toolboxes/sample_toolboxes_shell_and_web_iq_async.py new file mode 100644 index 000000000000..c5b1de54c7ce --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/toolboxes/sample_toolboxes_shell_and_web_iq_async.py @@ -0,0 +1,93 @@ +# 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 containing a shell tool + backed by an automatically provisioned container and a WebIQ preview tool + using the asynchronous AIProjectClient. + +USAGE: + python sample_toolboxes_shell_and_web_iq_async.py + + Before running the sample: + + pip install "azure-ai-projects>=2.6.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) WEB_IQ_PROJECT_CONNECTION_ID - The fully-qualified resource ID of the WebIQ project connection. +""" + +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 ( + ShellToolboxTool, + ToolboxShellContainerAutoEnvironment, + ToolboxShellNetworkPolicyDisabled, + WebIQPreviewToolboxTool, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +toolbox_name = "toolbox_with_shell_and_web_iq" + + +async def main() -> None: + async with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, + ): + created = None + try: + try: + await project_client.toolboxes.delete(name=toolbox_name) + except ResourceNotFoundError: + pass + + created = await project_client.toolboxes.create_version( + name=toolbox_name, + description="Toolbox with shell and WebIQ tools.", + tools=[ + ShellToolboxTool( + name="shell", + description="Run shell commands in an automatically provisioned container.", + environment=ToolboxShellContainerAutoEnvironment( + memory_limit="4g", + network_policy=ToolboxShellNetworkPolicyDisabled(), + ), + ), + WebIQPreviewToolboxTool( + name="web_iq", + description="Use WebIQ through a project connection.", + project_connection_id=os.environ["WEB_IQ_PROJECT_CONNECTION_ID"], + server_label="web-iq", + require_approval="always", + ), + ], + ) + print(f"Created toolbox `{created.name}` version {created.version}") + + fetched = await project_client.toolboxes.get_version(name=toolbox_name, version=created.version) + for tool in fetched.tools or []: + print(f"Tool `{tool.name}` has type `{tool.type}`") + finally: + if created is not None: + await project_client.toolboxes.delete(name=toolbox_name) + print("Toolbox deleted") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/sdk/ai/azure-ai-projects/tests/agents/tools/test_agent_web_iq.py b/sdk/ai/azure-ai-projects/tests/agents/tools/test_agent_web_iq.py new file mode 100644 index 000000000000..955e5ac87c9f --- /dev/null +++ b/sdk/ai/azure-ai-projects/tests/agents/tools/test_agent_web_iq.py @@ -0,0 +1,54 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest + +from devtools_testutils import recorded_by_proxy + +from azure.ai.projects.models import PromptAgentDefinition, ToolType, WebIQPreviewTool + +from test_base import TestBase, servicePreparer + + +@pytest.mark.skip(reason="TODO(WebIQ): enable after Test Proxy recordings are added.") +class TestAgentWebIQ(TestBase): + + @servicePreparer() + @recorded_by_proxy + def test_agent_web_iq(self, **kwargs) -> None: + model_name = kwargs.get("foundry_model_name") + connection_id = kwargs.get("web_iq_project_connection_id") + assert isinstance(model_name, str) + assert isinstance(connection_id, str) + + agent_name = "web-iq-agent" + created = None + + with self.create_client(operation_group="agents", allow_preview=True, **kwargs) as project_client: + try: + created = project_client.agents.create_version( + agent_name=agent_name, + definition=PromptAgentDefinition( + model=model_name, + instructions="Use WebIQ to answer questions.", + tools=[WebIQPreviewTool(project_connection_id=connection_id)], + ), + ) + self._validate_agent_version(created, expected_name=agent_name) + + fetched = project_client.agents.get_version( + agent_name=agent_name, + agent_version=created.version, + ) + assert isinstance(fetched.definition, PromptAgentDefinition) + assert any(tool.type == ToolType.WEB_IQ_PREVIEW for tool in fetched.definition.tools or []) + finally: + if created is not None: + project_client.agents.delete_version( + agent_name=agent_name, + agent_version=created.version, + force=True, + ) diff --git a/sdk/ai/azure-ai-projects/tests/agents/tools/test_agent_web_iq_async.py b/sdk/ai/azure-ai-projects/tests/agents/tools/test_agent_web_iq_async.py new file mode 100644 index 000000000000..2f8e4eb22134 --- /dev/null +++ b/sdk/ai/azure-ai-projects/tests/agents/tools/test_agent_web_iq_async.py @@ -0,0 +1,54 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest + +from devtools_testutils.aio import recorded_by_proxy_async + +from azure.ai.projects.models import PromptAgentDefinition, ToolType, WebIQPreviewTool + +from test_base import TestBase, servicePreparer + + +@pytest.mark.skip(reason="TODO(WebIQ): enable after Test Proxy recordings are added.") +class TestAgentWebIQAsync(TestBase): + + @servicePreparer() + @recorded_by_proxy_async + async def test_agent_web_iq_async(self, **kwargs) -> None: + model_name = kwargs.get("foundry_model_name") + connection_id = kwargs.get("web_iq_project_connection_id") + assert isinstance(model_name, str) + assert isinstance(connection_id, str) + + agent_name = "web-iq-agent" + created = None + + async with self.create_async_client(operation_group="agents", allow_preview=True, **kwargs) as project_client: + try: + created = await project_client.agents.create_version( + agent_name=agent_name, + definition=PromptAgentDefinition( + model=model_name, + instructions="Use WebIQ to answer questions.", + tools=[WebIQPreviewTool(project_connection_id=connection_id)], + ), + ) + self._validate_agent_version(created, expected_name=agent_name) + + fetched = await project_client.agents.get_version( + agent_name=agent_name, + agent_version=created.version, + ) + assert isinstance(fetched.definition, PromptAgentDefinition) + assert any(tool.type == ToolType.WEB_IQ_PREVIEW for tool in fetched.definition.tools or []) + finally: + if created is not None: + await project_client.agents.delete_version( + agent_name=agent_name, + agent_version=created.version, + force=True, + ) 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 d34aab6783b8..5f8a12bc362a 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 @@ -45,7 +45,7 @@ "schedules": "Schedules=V1Preview", "skills": "Skills=V1Preview", "datasets": "DataGenerationJobs=V1Preview", - "agents": "WorkflowAgents=V1Preview,ExternalAgents=V1Preview,DraftAgents=V1Preview,AgentsOptimization=V2Preview", + "agents": "WorkflowAgents=V1Preview,ExternalAgents=V1Preview,DraftAgents=V1Preview,AgentsOptimization=V2Preview,ModelRouterControls=V1Preview", } # Methods on .beta sub-clients that are NOT simple one-HTTP-call wrappers and @@ -83,7 +83,7 @@ # The test id is derived automatically from method_name. pytest.param( "agents.create_version", - "WorkflowAgents=V1Preview,ExternalAgents=V1Preview,DraftAgents=V1Preview,AgentsOptimization=V2Preview", + "WorkflowAgents=V1Preview,ExternalAgents=V1Preview,DraftAgents=V1Preview,AgentsOptimization=V2Preview,ModelRouterControls=V1Preview", ), pytest.param( "evaluation_rules.create_or_update", 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 ee9d60f80fdd..97745de2603e 100644 --- a/sdk/ai/azure-ai-projects/tests/samples/test_samples.py +++ b/sdk/ai/azure-ai-projects/tests/samples/test_samples.py @@ -49,6 +49,7 @@ class TestSamples(AzureRecordedTestCase): "sample_agent_openapi.py", # 400 2/28/2026 validation/tool_user_error; failing weather GET curl call in OpenAPI tool "sample_agent_memory_search.py", # Skipped until re-enabled and recorded on Foundry endpoint that supports the new versioning schema "sample_agent_to_agent.py", # Skipped not sample should work, but not able to obtain a project endpoint that work with a2a at this moment + "sample_agent_web_iq.py", # Recording not yet available. ], ), ) @@ -353,7 +354,9 @@ def test_skills_samples(self, sample_path: str, **kwargs) -> None: "sample_path", get_sample_paths( "toolboxes", - samples_to_skip=[], + samples_to_skip=[ + "sample_toolboxes_shell_and_web_iq.py", # Recording not yet available. + ], ), ) @servicePreparer() 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 7fdbe416f0d4..c5daf6cfda86 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 @@ -30,6 +30,7 @@ class TestSamplesAsync(AzureRecordedTestCase): samples_to_skip=[ "sample_agent_computer_use_async.py", "sample_agent_memory_search_async.py", # Skipped until re-enabled and recorded on Foundry endpoint that supports the new versioning schema + "sample_agent_web_iq_async.py", # Recording not yet available. ], ), ) @@ -301,7 +302,9 @@ async def test_skills_samples(self, sample_path: str, **kwargs) -> None: "sample_path", get_async_sample_paths( "toolboxes", - samples_to_skip=[], + samples_to_skip=[ + "sample_toolboxes_shell_and_web_iq_async.py", # Recording not yet available. + ], ), ) @servicePreparer() diff --git a/sdk/ai/azure-ai-projects/tests/test_base.py b/sdk/ai/azure-ai-projects/tests/test_base.py index 4069479fdcb3..6eb9dbbf6f3f 100644 --- a/sdk/ai/azure-ai-projects/tests/test_base.py +++ b/sdk/ai/azure-ai-projects/tests/test_base.py @@ -57,6 +57,7 @@ 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", work_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-work-iq-connection", + web_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-web-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", @@ -73,6 +74,7 @@ bing_custom_user_input="Tell me more about foundry agent service", fabric_iq_user_input="Tell me weather history in London, Ohio", work_iq_user_input="What is the Work IQ?", + web_iq_user_input="Summarize the latest information available through WebIQ.", 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", diff --git a/sdk/ai/azure-ai-projects/tests/toolboxes/test_toolboxes.py b/sdk/ai/azure-ai-projects/tests/toolboxes/test_toolboxes.py new file mode 100644 index 000000000000..926deb79cb94 --- /dev/null +++ b/sdk/ai/azure-ai-projects/tests/toolboxes/test_toolboxes.py @@ -0,0 +1,70 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest + +from azure.core.exceptions import ResourceNotFoundError +from devtools_testutils import recorded_by_proxy + +from azure.ai.projects.models import ( + ShellToolboxTool, + ToolboxShellContainerAutoEnvironment, + ToolboxShellNetworkPolicyDisabled, + ToolboxToolType, + WebIQPreviewToolboxTool, +) + +from test_base import TestBase, servicePreparer + + +@pytest.mark.skip(reason="TODO(shell and WebIQ toolbox tools): enable after Test Proxy recordings are added.") +class TestToolboxes(TestBase): + + @servicePreparer() + @recorded_by_proxy + def test_shell_and_web_iq_tools(self, **kwargs) -> None: + connection_id = kwargs.get("web_iq_project_connection_id") + assert isinstance(connection_id, str) + + toolbox_name = "test-toolbox-shell-web-iq" + created = None + + with self.create_client(allow_preview=True, **kwargs) as project_client: + try: + try: + project_client.toolboxes.delete(name=toolbox_name) + except ResourceNotFoundError: + pass + + created = project_client.toolboxes.create_version( + name=toolbox_name, + description="Toolbox containing shell and WebIQ tools.", + tools=[ + ShellToolboxTool( + name="shell", + environment=ToolboxShellContainerAutoEnvironment( + memory_limit="4g", + network_policy=ToolboxShellNetworkPolicyDisabled(), + ), + ), + WebIQPreviewToolboxTool( + name="web_iq", + project_connection_id=connection_id, + server_label="web-iq", + require_approval="always", + ), + ], + ) + assert created.name == toolbox_name + assert created.version + + fetched = project_client.toolboxes.get_version(name=toolbox_name, version=created.version) + tool_types = {tool.type for tool in fetched.tools or []} + assert ToolboxToolType.SHELL in tool_types + assert ToolboxToolType.WEB_IQ_PREVIEW in tool_types + finally: + if created is not None: + project_client.toolboxes.delete(name=toolbox_name) diff --git a/sdk/ai/azure-ai-projects/tests/toolboxes/test_toolboxes_async.py b/sdk/ai/azure-ai-projects/tests/toolboxes/test_toolboxes_async.py new file mode 100644 index 000000000000..313d7d80fe7c --- /dev/null +++ b/sdk/ai/azure-ai-projects/tests/toolboxes/test_toolboxes_async.py @@ -0,0 +1,70 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest + +from azure.core.exceptions import ResourceNotFoundError +from devtools_testutils.aio import recorded_by_proxy_async + +from azure.ai.projects.models import ( + ShellToolboxTool, + ToolboxShellContainerAutoEnvironment, + ToolboxShellNetworkPolicyDisabled, + ToolboxToolType, + WebIQPreviewToolboxTool, +) + +from test_base import TestBase, servicePreparer + + +@pytest.mark.skip(reason="TODO(shell and WebIQ toolbox tools): enable after Test Proxy recordings are added.") +class TestToolboxesAsync(TestBase): + + @servicePreparer() + @recorded_by_proxy_async + async def test_shell_and_web_iq_tools_async(self, **kwargs) -> None: + connection_id = kwargs.get("web_iq_project_connection_id") + assert isinstance(connection_id, str) + + toolbox_name = "test-toolbox-shell-web-iq" + created = None + + async with self.create_async_client(allow_preview=True, **kwargs) as project_client: + try: + try: + await project_client.toolboxes.delete(name=toolbox_name) + except ResourceNotFoundError: + pass + + created = await project_client.toolboxes.create_version( + name=toolbox_name, + description="Toolbox containing shell and WebIQ tools.", + tools=[ + ShellToolboxTool( + name="shell", + environment=ToolboxShellContainerAutoEnvironment( + memory_limit="4g", + network_policy=ToolboxShellNetworkPolicyDisabled(), + ), + ), + WebIQPreviewToolboxTool( + name="web_iq", + project_connection_id=connection_id, + server_label="web-iq", + require_approval="always", + ), + ], + ) + assert created.name == toolbox_name + assert created.version + + fetched = await project_client.toolboxes.get_version(name=toolbox_name, version=created.version) + tool_types = {tool.type for tool in fetched.tools or []} + assert ToolboxToolType.SHELL in tool_types + assert ToolboxToolType.WEB_IQ_PREVIEW in tool_types + finally: + if created is not None: + await project_client.toolboxes.delete(name=toolbox_name) diff --git a/sdk/ai/azure-ai-projects/tsp-location.yaml b/sdk/ai/azure-ai-projects/tsp-location.yaml index 259cd043ef1f..1d37c7f38390 100644 --- a/sdk/ai/azure-ai-projects/tsp-location.yaml +++ b/sdk/ai/azure-ai-projects/tsp-location.yaml @@ -1,5 +1,5 @@ directory: specification/ai-foundry/data-plane/Foundry/src/sdk-python-js-azure-ai-projects -commit: 9a1ee382eb32ff2af52911bf3106d97d0a6ab226 +commit: 592d36dbd539e1365d0e8c0e993daf8ffeda29ef repo: Azure/azure-rest-api-specs additionalDirectories: - specification/ai-foundry/data-plane/Foundry/src/agents