From 5a47c325e75ddbec65fb4253047aa78f1653eda1 Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Tue, 16 Jun 2026 07:06:19 -0700 Subject: [PATCH 1/9] trim --- .../azure-ai-projects/post-emitter-fixes.cmd | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd b/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd index 1001de9c2f8e..ac25de27a5a4 100644 --- a/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd +++ b/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd @@ -14,27 +14,6 @@ REM - The emitter uses lower case "i" in "Ai". I want to keep it upper case in t REM - We want a vanity link for the "repository" value, deep linking to the SDK folder (not root of repo): https://aka.ms/azsdk/azure-ai-projects-v2/python/code git restore pyproject.toml -REM Rename `"items_property": items`, to `"items": items` in search_memories and begin_update_memories methods. "items" is specified in TypeSpec, but Python emitter does not allow it. -powershell -Command "(Get-Content azure\ai\projects\aio\operations\_operations.py) -replace '\"items_property\": items', '\"items\": items' | Set-Content azure\ai\projects\aio\operations\_operations.py" -powershell -Command "(Get-Content azure\ai\projects\operations\_operations.py) -replace '\"items_property\": items', '\"items\": items' | Set-Content azure\ai\projects\operations\_operations.py" - -REM Rename WEB_SEARCH_PREVIEW2025_03_11 enum member to WEB_SEARCH_PREVIEW_2025_03_11, to match actual string value -powershell -Command "(Get-Content azure\ai\projects\models\_enums.py) -replace 'WEB_SEARCH_PREVIEW2025_03_11', 'WEB_SEARCH_PREVIEW_2025_03_11' | Set-Content azure\ai\projects\models\_enums.py" -powershell -Command "(Get-Content azure\ai\projects\models\_models.py) -replace 'WEB_SEARCH_PREVIEW2025_03_11', 'WEB_SEARCH_PREVIEW_2025_03_11' | Set-Content azure\ai\projects\models\_models.py" - -REM Rename DEFAULT2024_11_15 to DEFAULT_2024_11_15 -powershell -Command "(Get-Content azure\ai\projects\models\_enums.py) -replace 'DEFAULT2024_11_15', 'DEFAULT_2024_11_15' | Set-Content azure\ai\projects\models\_enums.py" - -REM Rename `A2_A` to `A2A` in enum class AgentEndpointProtocol and ToolType -powershell -Command "(Get-Content azure\ai\projects\models\_enums.py) -replace 'A2_A', 'A2A' | Set-Content azure\ai\projects\models\_enums.py" -powershell -Command "(Get-Content azure\ai\projects\models\_models.py) -replace 'A2_A', 'A2A' | Set-Content azure\ai\projects\models\_models.py" - -REM Rename MEMORY1_GB/MEMORY4_GB/MEMORY16_GB/MEMORY64_GB to MEMORY_1GB/MEMORY_4GB/MEMORY_16GB/MEMORY_64GB in enum class ContainerMemoryLimit -REM For some reason these TypeSpec decorators stopped working. Need to understand why: @@clientName(OpenAI.ContainerMemoryLimit.`1g`, "MEMORY_1GB", "python"); -powershell -Command "(Get-Content azure\ai\projects\models\_enums.py) -replace 'MEMORY1_GB', 'MEMORY_1GB' | Set-Content azure\ai\projects\models\_enums.py" -powershell -Command "(Get-Content azure\ai\projects\models\_enums.py) -replace 'MEMORY4_GB', 'MEMORY_4GB' | Set-Content azure\ai\projects\models\_enums.py" -powershell -Command "(Get-Content azure\ai\projects\models\_enums.py) -replace 'MEMORY16_GB', 'MEMORY_16GB' | Set-Content azure\ai\projects\models\_enums.py" -powershell -Command "(Get-Content azure\ai\projects\models\_enums.py) -replace 'MEMORY64_GB', 'MEMORY_64GB' | Set-Content azure\ai\projects\models\_enums.py" REM Edit both _operations.py files to fix missing Foundry-Features HTTP request header in continued list paging calls. Add: REM headers=_headers From 5142cbf62f2ee1eff3ffca31bbfe3a4c1ad57d0a Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Tue, 16 Jun 2026 07:06:58 -0700 Subject: [PATCH 2/9] re-emit, without running post emitter script --- .../azure-ai-projects/apiview-properties.json | 2 +- .../azure/ai/projects/_utils/utils.py | 1 + .../ai/projects/aio/operations/_operations.py | 36 +++++++++---------- .../azure/ai/projects/models/_models.py | 24 +++++++------ .../ai/projects/operations/_operations.py | 36 +++++++++---------- sdk/ai/azure-ai-projects/pyproject.toml | 20 +++++------ .../tests/samples/llm_instructions.py | 32 ++++++++++++----- 7 files changed, 82 insertions(+), 69 deletions(-) diff --git a/sdk/ai/azure-ai-projects/apiview-properties.json b/sdk/ai/azure-ai-projects/apiview-properties.json index 37f1180762a5..56060921e407 100644 --- a/sdk/ai/azure-ai-projects/apiview-properties.json +++ b/sdk/ai/azure-ai-projects/apiview-properties.json @@ -481,5 +481,5 @@ "azure.ai.projects.operations.IndexesOperations.create_or_update": "Azure.AI.Projects.Indexes.createOrUpdateVersion", "azure.ai.projects.aio.operations.IndexesOperations.create_or_update": "Azure.AI.Projects.Indexes.createOrUpdateVersion" }, - "CrossLanguageVersion": "0ee459332041" + "CrossLanguageVersion": "54953c829a31" } \ No newline at end of file diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py b/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py index c91d6470e2bf..9ebe3cda3180 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py @@ -11,6 +11,7 @@ from .._utils.model_base import Model, SdkJSONEncoder + # file-like tuple could be `(filename, IO (or bytes))` or `(filename, IO (or bytes), content_type)` FileContent = Union[str, bytes, IO[str], IO[bytes]] diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py index e278f6761204..7f0eef4078a5 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py @@ -4108,23 +4108,29 @@ async def get_session_log_stream( Each SSE frame contains: * `event`: always `"log"` - * `data`: a plain-text log line (currently JSON-formatted, but the schema is not contractual and may include additional keys or change format over time; clients should treat it as an opaque string) + * `data`: a plain-text log line (currently JSON-formatted, but the schema + is not contractual and may include additional keys or change format + over time — clients should treat it as an opaque string) Example SSE frames: .. code-block:: event: log - data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting FoundryCBAgent server on port 8088"} + data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting + FoundryCBAgent server on port 8088"} event: log - data: {"timestamp":"2026-03-10T09:33:17.130Z","stream":"stderr","message":"INFO: Application startup complete."} + data: {"timestamp":"2026-03-10T09:33:17.130Z","stream":"stderr","message":"INFO: Application + startup complete."} event: log - data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully connected to container"} + data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully + connected to container"} event: log - data: {"timestamp":"2026-03-10T09:35:52.714Z","stream":"status","message":"No logs since last 60 seconds"} + data: {"timestamp":"2026-03-10T09:35:52.714Z","stream":"status","message":"No logs since + last 60 seconds"} The stream remains open until the client disconnects or the server terminates the connection. Clients should handle reconnection as needed. @@ -4166,7 +4172,7 @@ async def get_session_log_stream( _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = True + _stream = kwargs.pop("stream", False) pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) @@ -9013,14 +9019,6 @@ def list_memories( 304: ResourceNotModifiedError, } error_map.update(kwargs.pop("error_map", {}) or {}) - - # BUG? These lines were inside the prepare_request() method. Moved here instead. - if body is _Unset: - if scope is _Unset: - raise TypeError("missing required argument: scope") - body = {"scope": scope} - body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/json" _content = None if isinstance(body, (IOBase, bytes)): @@ -9029,11 +9027,11 @@ def list_memories( _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore def prepare_request(_continuation_token=None): - # if body is _Unset: - # if scope is _Unset: - # raise TypeError("missing required argument: scope") - # body = {"scope": scope} - # body = {k: v for k, v in body.items() if v is not None} + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = {"scope": scope} + body = {k: v for k, v in body.items() if v is not None} _request = build_beta_memory_stores_list_memories_request( name=name, 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 f5f7d8f14d09..2af6c71c616b 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 @@ -10824,15 +10824,15 @@ class OptimizationInlineDatasetInput(OptimizationDatasetInput, discriminator="in :ivar type: Dataset input type discriminator. Required. Inline dataset — items are provided directly in the request body. :vartype type: str or ~azure.ai.projects.models.INLINE - :ivar items_property: Dataset items. Required. - :vartype items_property: list[~azure.ai.projects.models.OptimizationDatasetItem] + :ivar dataset_items: Dataset items. Required. + :vartype dataset_items: list[~azure.ai.projects.models.OptimizationDatasetItem] """ type: Literal[OptimizationDatasetInputType.INLINE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """Dataset input type discriminator. Required. Inline dataset — items are provided directly in the request body.""" - items_property: list["_models.OptimizationDatasetItem"] = rest_field( - name="items", visibility=["read", "create", "update", "delete", "query"], original_tsp_name="items" + dataset_items: list["_models.OptimizationDatasetItem"] = rest_field( + name="items", visibility=["read", "create", "update", "delete", "query"] ) """Dataset items. Required.""" @@ -10840,7 +10840,7 @@ class OptimizationInlineDatasetInput(OptimizationDatasetInput, discriminator="in def __init__( self, *, - items_property: list["_models.OptimizationDatasetItem"], + dataset_items: list["_models.OptimizationDatasetItem"], ) -> None: ... @overload @@ -12699,10 +12699,12 @@ class SessionLogEvent(_Model): .. code-block:: event: log - data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting server on port 18080"} + data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting server + on port 18080"} event: log - data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully connected to container"} + data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully + connected to container"}. :ivar event: The SSE event type. Currently ``log``, but additional event types may be added in the future. Clients should ignore unrecognized event types. Required. "log" @@ -13929,7 +13931,7 @@ class ToolChoiceAllowed(ToolChoiceParam, discriminator="allowed_tools"): or a Literal["required"] type. :vartype mode: str or str :ivar tools: A list of tool definitions that the model should be allowed to call. For the - Responses API, the list of tool definitions might look like the following. Required. + Responses API, the list of tool definitions might look like: .. code-block:: json @@ -13937,7 +13939,7 @@ class ToolChoiceAllowed(ToolChoiceParam, discriminator="allowed_tools"): { "type": "function", "name": "get_weather" }, { "type": "mcp", "server_label": "deepwiki" }, { "type": "image_generation" } - ] + ]. Required. :vartype tools: list[dict[str, any]] """ @@ -13950,7 +13952,7 @@ class ToolChoiceAllowed(ToolChoiceParam, discriminator="allowed_tools"): Literal[\"required\"] type.""" tools: list[dict[str, Any]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """A list of tool definitions that the model should be allowed to call. For the Responses API, the - list of tool definitions might look like the following. Required. + list of tool definitions might look like: .. code-block:: json @@ -13958,7 +13960,7 @@ class ToolChoiceAllowed(ToolChoiceParam, discriminator="allowed_tools"): { \"type\": \"function\", \"name\": \"get_weather\" }, { \"type\": \"mcp\", \"server_label\": \"deepwiki\" }, { \"type\": \"image_generation\" } - ]""" + ]. Required.""" @overload def __init__( diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py index 09319efc0d67..64f92690e19f 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py @@ -7535,23 +7535,29 @@ def get_session_log_stream( Each SSE frame contains: * `event`: always `"log"` - * `data`: a plain-text log line (currently JSON-formatted, but the schema is not contractual and may include additional keys or change format over time; clients should treat it as an opaque string) + * `data`: a plain-text log line (currently JSON-formatted, but the schema + is not contractual and may include additional keys or change format + over time — clients should treat it as an opaque string) Example SSE frames: .. code-block:: event: log - data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting FoundryCBAgent server on port 8088"} + data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting + FoundryCBAgent server on port 8088"} event: log - data: {"timestamp":"2026-03-10T09:33:17.130Z","stream":"stderr","message":"INFO: Application startup complete."} + data: {"timestamp":"2026-03-10T09:33:17.130Z","stream":"stderr","message":"INFO: Application + startup complete."} event: log - data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully connected to container"} + data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully + connected to container"} event: log - data: {"timestamp":"2026-03-10T09:35:52.714Z","stream":"status","message":"No logs since last 60 seconds"} + data: {"timestamp":"2026-03-10T09:35:52.714Z","stream":"status","message":"No logs since + last 60 seconds"} The stream remains open until the client disconnects or the server terminates the connection. Clients should handle reconnection as needed. @@ -7593,7 +7599,7 @@ def get_session_log_stream( _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = True + _stream = kwargs.pop("stream", False) pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) @@ -12436,14 +12442,6 @@ def list_memories( 304: ResourceNotModifiedError, } error_map.update(kwargs.pop("error_map", {}) or {}) - - # BUG? These lines were inside the prepare_request() method. Moved here instead. - if body is _Unset: - if scope is _Unset: - raise TypeError("missing required argument: scope") - body = {"scope": scope} - body = {k: v for k, v in body.items() if v is not None} - content_type = content_type or "application/json" _content = None if isinstance(body, (IOBase, bytes)): @@ -12452,11 +12450,11 @@ def list_memories( _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore def prepare_request(_continuation_token=None): - # if body is _Unset: - # if scope is _Unset: - # raise TypeError("missing required argument: scope") - # body = {"scope": scope} - # body = {k: v for k, v in body.items() if v is not None} + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = {"scope": scope} + body = {k: v for k, v in body.items() if v is not None} _request = build_beta_memory_stores_list_memories_request( name=name, diff --git a/sdk/ai/azure-ai-projects/pyproject.toml b/sdk/ai/azure-ai-projects/pyproject.toml index 26eac48e1123..6524161e8da2 100644 --- a/sdk/ai/azure-ai-projects/pyproject.toml +++ b/sdk/ai/azure-ai-projects/pyproject.toml @@ -14,21 +14,20 @@ name = "azure-ai-projects" authors = [ { name = "Microsoft Corporation", email = "azpysdkhelp@microsoft.com" }, ] -description = "Microsoft Corporation Azure AI Projects Client Library for Python" +description = "Microsoft Corporation Azure Ai Projects Client Library for Python" license = "MIT" classifiers = [ - "Development Status :: 5 - Production/Stable", + "Development Status :: 4 - Beta", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", ] -requires-python = ">=3.9" +requires-python = ">=3.10" keywords = ["azure", "azure sdk"] dependencies = [ @@ -44,7 +43,7 @@ dynamic = [ ] [project.urls] -repository = "https://aka.ms/azsdk/azure-ai-projects-v2/python/code" +repository = "https://github.com/Azure/azure-sdk-for-python" [tool.setuptools.dynamic] version = {attr = "azure.ai.projects._version.VERSION"} @@ -52,13 +51,13 @@ readme = {file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown"} [tool.setuptools.packages.find] exclude = [ - "azure.ai", - "azure", - "doc*", - "generated_samples*", + "tests*", "generated_tests*", "samples*", - "tests*", + "generated_samples*", + "doc*", + "azure", + "azure.ai", ] [tool.setuptools.package-data] @@ -69,4 +68,3 @@ verifytypes = false [tool.azure-sdk-conda] in_bundle = false - diff --git a/sdk/ai/azure-ai-projects/tests/samples/llm_instructions.py b/sdk/ai/azure-ai-projects/tests/samples/llm_instructions.py index af98d794a98d..0e0f776441cf 100644 --- a/sdk/ai/azure-ai-projects/tests/samples/llm_instructions.py +++ b/sdk/ai/azure-ai-projects/tests/samples/llm_instructions.py @@ -18,7 +18,8 @@ from typing import Final -agent_tools_instructions: Final[str] = """ +agent_tools_instructions: Final[str] = ( + """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct for a tool-driven assistant workflow. @@ -43,9 +44,11 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() +) -memories_instructions: Final[str] = """ +memories_instructions: Final[str] = ( + """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct for a memories workflow. @@ -70,9 +73,11 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() +) -agents_instructions: Final[str] = """ +agents_instructions: Final[str] = ( + """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct. @@ -103,9 +108,11 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() +) -chat_completions_instructions: Final[str] = """ +chat_completions_instructions: Final[str] = ( + """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct for Chat Completions scenarios. @@ -124,9 +131,11 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() +) -resource_management_instructions: Final[str] = """ +resource_management_instructions: Final[str] = ( + """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct for resource-management samples (for example connections, files, and deployments). @@ -152,9 +161,11 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() +) -fine_tuning_instructions: Final[str] = """ +fine_tuning_instructions: Final[str] = ( + """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct for a fine-tuning workflow. @@ -178,9 +189,11 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() +) -evaluations_instructions: Final[str] = """ +evaluations_instructions: Final[str] = ( + """ We just ran Python code for an evaluation sample and captured print/log output in an attached log file (TXT). Your job: determine if the sample code executed to completion WITHOUT throwing an unhandled exception. @@ -202,9 +215,11 @@ Always respond with `reason` indicating the reason for the response. """.strip() +) -hosted_agents_instructions: Final[str] = """ +hosted_agents_instructions: Final[str] = ( + """ We just ran Python code for a hosted-agent sample and captured print/log output in an attached log file (TXT). Validate whether the sample executed correctly. @@ -226,6 +241,7 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() +) # Folder (under samples/) -> instructions. From 7a027ee3c71943be81df9cc174cbf4f982391bdf Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Tue, 16 Jun 2026 07:35:26 -0700 Subject: [PATCH 3/9] New PostEmitter.ps1 --- sdk/ai/azure-ai-projects/PostEmitter.ps1 | 117 +++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 sdk/ai/azure-ai-projects/PostEmitter.ps1 diff --git a/sdk/ai/azure-ai-projects/PostEmitter.ps1 b/sdk/ai/azure-ai-projects/PostEmitter.ps1 new file mode 100644 index 000000000000..c6c9624a8c2a --- /dev/null +++ b/sdk/ai/azure-ai-projects/PostEmitter.ps1 @@ -0,0 +1,117 @@ +# +# To emit from TypeSpec, run this in the current folder: +# +# tsp-client update --debug ==> to use the commit mentioned in the local tsp-location.yaml to generate +# tsp-client update --debug --save-inputs" ==> To save the local folder TempTypeSpecFiles +# tsp-client update --debug --local-spec-repo " ==> to use your local TypeSpec folder. Path is like: +# D:\src\azure-rest-api-specs\specification\ai-foundry\data-plane\Foundry\src\sdk-python-js-azure-ai-projects +# +# Then run this script to "fix" the emitted code. +# + +# Revert emitted pyprojects.toml, since it overrides the following changes: +# - We added "Programming Language :: Python :: 3.14". The emitter removes it. +# - The emitter uses lower case "i" in "Ai". I want to keep it upper case in the description field: "Microsoft Corporation Azure AI Projects Client Library for Python". +# - We want a vanity link for the "repository" value, deep linking to the SDK folder (not root of repo): https://aka.ms/azsdk/azure-ai-projects-v2/python/code +git restore pyproject.toml + + +# Edit both _operations.py files to fix missing Foundry-Features HTTP request header in continued list paging calls. Add: +# headers=_headers +# to the end of each of these lines in the BetaXxxOperations classes (do not do this in GA operations classes!) +# "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params +# In emitted code, these first 7 of those lines are associated with GA operations, so start the replacement +# from the 8th occurrence onward. +$gaCount = 7 +$old = [char]34 + 'GET' + [char]34 + ', urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params' +$new = $old + ', headers=_headers' +foreach ($f in 'azure\ai\projects\aio\operations\_operations.py', 'azure\ai\projects\operations\_operations.py') { + $c = Get-Content $f -Raw + $parts = $c -split [regex]::Escape($old) + $r = $parts[0] + for ($i = 1; $i -lt $parts.Length; $i++) { + if ($i -le $gaCount) { + $r += $old + $parts[$i] + } else { + $r += $new + $parts[$i] + } + } + Set-Content $f $r -NoNewline +} + +# Force streaming in get_session_log_stream for both sync and async operations. +$files = 'azure\ai\projects\operations\_operations.py', 'azure\ai\projects\aio\operations\_operations.py' +foreach ($f in $files) { + $lines = Get-Content $f + $inFunc = $false + for ($i = 0; $i -lt $lines.Length; $i++) { + if ($lines[$i] -match '^\s*(async\s+)?def\s+get_session_log_stream\(') { + $inFunc = $true + continue + } + if ($inFunc -and $lines[$i] -match '^\s*(async\s+)?def\s+\w+\(') { + $inFunc = $false + } + if ($inFunc -and $lines[$i] -match 'kwargs\.pop\(.+stream.+False\)') { + $indent = ([regex]::Match($lines[$i], '^\s*')).Value + $lines[$i] = $indent + '_stream = True' + } + } + Set-Content $f $lines +} + +# Fix Sphinx issue in class ToolChoiceAllowed, in "tools" property doc string. The "Required" cannot come at the end of the code-block. +# move it to the end of the text before the code block, and make sure there are no periods after "]". +# .. code-block:: json +# +# [ +# { "type": "function", "name": "get_weather" }, +# { "type": "mcp", "server_label": "deepwiki" }, +# { "type": "image_generation" } +# ]. Required. +(Get-Content azure\ai\projects\models\_models.py) -replace 'Responses API, the list of tool definitions might look like:', 'Responses API, the list of tool definitions might look like the following. Required.' | Set-Content azure\ai\projects\models\_models.py +(Get-Content azure\ai\projects\models\_models.py) -replace 'list of tool definitions might look like:', 'list of tool definitions might look like the following. Required.' | Set-Content azure\ai\projects\models\_models.py +(Get-Content azure\ai\projects\models\_models.py) -replace ' \]\. Required\.', ' ]' | Set-Content azure\ai\projects\models\_models.py + +# Fix Sphinx docutils warnings in class SessionLogEvent: the generated docstring wraps two long +# ``data:`` JSON lines mid-string inside a ``.. code-block::`` section. The wrapped continuation +# lines have wrong indentation (4 spaces instead of 7), causing "unexpected unindent" warnings. +# Join each broken pair back into one line. +$f = 'azure\ai\projects\models\_models.py' +$c = Get-Content $f -Raw +$c = $c -replace '(Starting server)\r?\n[ \t]+(on port 18080)', '$1 $2' +$c = $c -replace '(Successfully)\r?\n[ \t]+(connected to container\"})\.?', '$1 $2' +Set-Content $f $c -NoNewline +$lines = Get-Content $f +$out = @() +foreach ($line in $lines) { + if ($line -match '^\s*on port 18080' -and $line -notmatch 'data:') { continue } + if ($line -match '^\s*connected to container' -and $line -notmatch 'data:') { continue } + if ($line -match '^\s*data: .*2026-03-10T09:33:17.121Z') { + $out += (' ' + $line.TrimStart()) + continue + } + if ($line -match '^\s*data: .*2026-03-10T09:34:52.714Z') { + $out += (' ' + $line.TrimStart()) + continue + } + $out += $line +} +Set-Content $f $out + +# Fix Sphinx docutils warnings in get_session_log_stream docstrings (sync + async). +# The emitter wraps bullet/code-block lines with insufficient indentation. +$files = 'azure\ai\projects\operations\_operations.py', 'azure\ai\projects\aio\operations\_operations.py' +foreach ($f in $files) { + $c = Get-Content $f -Raw + $c = $c -replace 'schema\r?\n\s+is not contractual and may include additional keys or change format\r?\n\s+over time [^\r\n]*clients should treat it as an opaque string\)', 'schema is not contractual and may include additional keys or change format over time; clients should treat it as an opaque string)' + $c = $c -replace '(message\":\"Starting)\r?\n\s+(FoundryCBAgent server on port 8088\"})', '$1 $2' + $c = $c -replace '(message\":\"INFO: Application)\r?\n\s+(startup complete\.\"})', '$1 $2' + $c = $c -replace '(message\":\"Successfully)\r?\n\s+(connected to container\"})', '$1 $2' + $c = $c -replace '(message\":\"No logs since)\r?\n\s+(last 60 seconds\"})', '$1 $2' + Set-Content $f $c -NoNewline +} + +# Finishing by running 'black' tool to format code. +pip install black +black --config ../../../eng/black-pyproject.toml . From 9b5480f7c9e488dac17980a4ec3f65503aff5575 Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Tue, 16 Jun 2026 07:35:54 -0700 Subject: [PATCH 4/9] delete old script --- .../azure-ai-projects/post-emitter-fixes.cmd | 56 ------------------- 1 file changed, 56 deletions(-) delete mode 100644 sdk/ai/azure-ai-projects/post-emitter-fixes.cmd diff --git a/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd b/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd deleted file mode 100644 index ac25de27a5a4..000000000000 --- a/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd +++ /dev/null @@ -1,56 +0,0 @@ -REM -REM To emit from TypeSpec, run this in the current folder: -REM -REM tsp-client update --debug --local-spec-repo e:\src\azure-rest-api-specs\specification\ai-foundry\data-plane\Foundry -REM -REM (replace `e:\src\...` with the local folder containing up to date TypeSpec) -REM -REM Then run this script to "fix" the emitted code. -REM - -REM Revert emitted pyprojects.toml, since it overrides the following changes: -REM - We added "Programming Language :: Python :: 3.14". The emitter removes it. -REM - The emitter uses lower case "i" in "Ai". I want to keep it upper case in the description field: "Microsoft Corporation Azure AI Projects Client Library for Python". -REM - We want a vanity link for the "repository" value, deep linking to the SDK folder (not root of repo): https://aka.ms/azsdk/azure-ai-projects-v2/python/code -git restore pyproject.toml - - -REM Edit both _operations.py files to fix missing Foundry-Features HTTP request header in continued list paging calls. Add: -REM headers=_headers -REM to the end of each of these lines in the BetaXxxOperations classes (do not do this in GA operations classes!) -REM "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params -REM In emitted code, these first 7 of those lines are associated with GA operations, so start the replacement -REM from the 8th occurrence onward. -powershell -Command "$gaCount=7; $old=[char]34+'GET'+[char]34+', urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params'; $new=$old+', headers=_headers'; foreach ($f in 'azure\ai\projects\aio\operations\_operations.py','azure\ai\projects\operations\_operations.py') { $c=Get-Content $f -Raw; $parts=$c -split [regex]::Escape($old); $r=$parts[0]; for ($i=1; $i -lt $parts.Length; $i++) { if ($i -le $gaCount) { $r+=$old+$parts[$i] } else { $r+=$new+$parts[$i] } }; Set-Content $f $r -NoNewline }" - -REM Force streaming in get_session_log_stream for both sync and async operations. -powershell -Command "$files='azure\ai\projects\operations\_operations.py','azure\ai\projects\aio\operations\_operations.py'; foreach ($f in $files) { $lines=Get-Content $f; $inFunc=$false; for ($i=0; $i -lt $lines.Length; $i++) { if ($lines[$i] -match '^\s*(async\s+)?def\s+get_session_log_stream\(') { $inFunc=$true; continue }; if ($inFunc -and $lines[$i] -match '^\s*(async\s+)?def\s+\w+\(') { $inFunc=$false }; if ($inFunc -and $lines[$i] -match 'kwargs\.pop\(.+stream.+False\)') { $indent=([regex]::Match($lines[$i], '^\s*')).Value; $lines[$i]=$indent + '_stream = True' } }; Set-Content $f $lines }" - -REM Fix Sphinx issue in class ToolChoiceAllowed, in "tools" property doc string. The "Required" cannot come at the end of the code-block. -REM move it to the end of the text before the code block, and make sure there are no periods after "]". -REM .. code-block:: json -REM -REM [ -REM { "type": "function", "name": "get_weather" }, -REM { "type": "mcp", "server_label": "deepwiki" }, -REM { "type": "image_generation" } -REM ]. Required. -powershell -Command "(Get-Content azure\ai\projects\models\_models.py) -replace 'Responses API, the list of tool definitions might look like:', 'Responses API, the list of tool definitions might look like the following. Required.' | Set-Content azure\ai\projects\models\_models.py" -powershell -Command "(Get-Content azure\ai\projects\models\_models.py) -replace 'list of tool definitions might look like:', 'list of tool definitions might look like the following. Required.' | Set-Content azure\ai\projects\models\_models.py" -powershell -Command "(Get-Content azure\ai\projects\models\_models.py) -replace ' \]\. Required\.', ' ]' | Set-Content azure\ai\projects\models\_models.py" - -REM Fix Sphinx docutils warnings in class SessionLogEvent: the generated docstring wraps two long -REM ``data:`` JSON lines mid-string inside a ``.. code-block::`` section. The wrapped continuation -REM lines have wrong indentation (4 spaces instead of 7), causing "unexpected unindent" warnings. -REM Join each broken pair back into one line. -powershell -Command "$f='azure\ai\projects\models\_models.py'; $c=Get-Content $f -Raw; $c=$c -replace '(Starting server)\r?\n[ \t]+(on port 18080)', '$1 $2'; $c=$c -replace '(Successfully)\r?\n[ \t]+(connected to container\"})\.?', '$1 $2'; Set-Content $f $c -NoNewline; $lines=Get-Content $f; $out=@(); foreach ($line in $lines) { if ($line -match '^\s*on port 18080' -and $line -notmatch 'data:') { continue }; if ($line -match '^\s*connected to container' -and $line -notmatch 'data:') { continue }; if ($line -match '^\s*data: .*2026-03-10T09:33:17.121Z') { $out += (' ' + $line.TrimStart()); continue }; if ($line -match '^\s*data: .*2026-03-10T09:34:52.714Z') { $out += (' ' + $line.TrimStart()); continue }; $out += $line }; Set-Content $f $out" - -REM Fix Sphinx docutils warnings in get_session_log_stream docstrings (sync + async). -REM The emitter wraps bullet/code-block lines with insufficient indentation. -powershell -Command "$files='azure\ai\projects\operations\_operations.py','azure\ai\projects\aio\operations\_operations.py'; foreach ($f in $files) { $c=Get-Content $f -Raw; $c=$c -replace 'schema\r?\n\s+is not contractual and may include additional keys or change format\r?\n\s+over time [^\r\n]*clients should treat it as an opaque string\)', 'schema is not contractual and may include additional keys or change format over time; clients should treat it as an opaque string)'; $c=$c -replace '(message\":\"Starting)\r?\n\s+(FoundryCBAgent server on port 8088\"})', '$1 $2'; $c=$c -replace '(message\":\"INFO: Application)\r?\n\s+(startup complete\.\"})', '$1 $2'; $c=$c -replace '(message\":\"Successfully)\r?\n\s+(connected to container\"})', '$1 $2'; $c=$c -replace '(message\":\"No logs since)\r?\n\s+(last 60 seconds\"})', '$1 $2'; Set-Content $f $c -NoNewline }" - -REM Finishing by running 'black' tool to format code. -pip install black -black --config ../../../eng/black-pyproject.toml . - - From 2a4b60f2b785dfc27e5fffe8d9a0781133909dc5 Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Tue, 16 Jun 2026 07:39:24 -0700 Subject: [PATCH 5/9] After PostEmitter script --- sdk/ai/azure-ai-projects/PostEmitter.ps1 | 3 +- .../azure/ai/projects/_utils/utils.py | 1 - .../ai/projects/aio/operations/_operations.py | 18 ++++------- .../azure/ai/projects/models/_models.py | 14 ++++---- .../ai/projects/operations/_operations.py | 18 ++++------- .../tests/samples/llm_instructions.py | 32 +++++-------------- 6 files changed, 28 insertions(+), 58 deletions(-) diff --git a/sdk/ai/azure-ai-projects/PostEmitter.ps1 b/sdk/ai/azure-ai-projects/PostEmitter.ps1 index c6c9624a8c2a..bba7b1714319 100644 --- a/sdk/ai/azure-ai-projects/PostEmitter.ps1 +++ b/sdk/ai/azure-ai-projects/PostEmitter.ps1 @@ -6,7 +6,8 @@ # tsp-client update --debug --local-spec-repo " ==> to use your local TypeSpec folder. Path is like: # D:\src\azure-rest-api-specs\specification\ai-foundry\data-plane\Foundry\src\sdk-python-js-azure-ai-projects # -# Then run this script to "fix" the emitted code. +# Then run this script to "fix" the emitted code: +# powershell -ExecutionPolicy Bypass -File PostEmitter.ps1 # # Revert emitted pyprojects.toml, since it overrides the following changes: diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py b/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py index 9ebe3cda3180..c91d6470e2bf 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py @@ -11,7 +11,6 @@ from .._utils.model_base import Model, SdkJSONEncoder - # file-like tuple could be `(filename, IO (or bytes))` or `(filename, IO (or bytes), content_type)` FileContent = Union[str, bytes, IO[str], IO[bytes]] diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py index 7f0eef4078a5..8274ba529518 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py @@ -4108,29 +4108,23 @@ async def get_session_log_stream( Each SSE frame contains: * `event`: always `"log"` - * `data`: a plain-text log line (currently JSON-formatted, but the schema - is not contractual and may include additional keys or change format - over time — clients should treat it as an opaque string) + * `data`: a plain-text log line (currently JSON-formatted, but the schema is not contractual and may include additional keys or change format over time; clients should treat it as an opaque string) Example SSE frames: .. code-block:: event: log - data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting - FoundryCBAgent server on port 8088"} + data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting FoundryCBAgent server on port 8088"} event: log - data: {"timestamp":"2026-03-10T09:33:17.130Z","stream":"stderr","message":"INFO: Application - startup complete."} + data: {"timestamp":"2026-03-10T09:33:17.130Z","stream":"stderr","message":"INFO: Application startup complete."} event: log - data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully - connected to container"} + data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully connected to container"} event: log - data: {"timestamp":"2026-03-10T09:35:52.714Z","stream":"status","message":"No logs since - last 60 seconds"} + data: {"timestamp":"2026-03-10T09:35:52.714Z","stream":"status","message":"No logs since last 60 seconds"} The stream remains open until the client disconnects or the server terminates the connection. Clients should handle reconnection as needed. @@ -4172,7 +4166,7 @@ async def get_session_log_stream( _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = True pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) 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 2af6c71c616b..e6fb3a3e2bbf 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 @@ -12699,12 +12699,10 @@ class SessionLogEvent(_Model): .. code-block:: event: log - data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting server - on port 18080"} + data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting server on port 18080"} event: log - data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully - connected to container"}. + data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully connected to container"} :ivar event: The SSE event type. Currently ``log``, but additional event types may be added in the future. Clients should ignore unrecognized event types. Required. "log" @@ -13931,7 +13929,7 @@ class ToolChoiceAllowed(ToolChoiceParam, discriminator="allowed_tools"): or a Literal["required"] type. :vartype mode: str or str :ivar tools: A list of tool definitions that the model should be allowed to call. For the - Responses API, the list of tool definitions might look like: + Responses API, the list of tool definitions might look like the following. Required. .. code-block:: json @@ -13939,7 +13937,7 @@ class ToolChoiceAllowed(ToolChoiceParam, discriminator="allowed_tools"): { "type": "function", "name": "get_weather" }, { "type": "mcp", "server_label": "deepwiki" }, { "type": "image_generation" } - ]. Required. + ] :vartype tools: list[dict[str, any]] """ @@ -13952,7 +13950,7 @@ class ToolChoiceAllowed(ToolChoiceParam, discriminator="allowed_tools"): Literal[\"required\"] type.""" tools: list[dict[str, Any]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """A list of tool definitions that the model should be allowed to call. For the Responses API, the - list of tool definitions might look like: + list of tool definitions might look like the following. Required. .. code-block:: json @@ -13960,7 +13958,7 @@ class ToolChoiceAllowed(ToolChoiceParam, discriminator="allowed_tools"): { \"type\": \"function\", \"name\": \"get_weather\" }, { \"type\": \"mcp\", \"server_label\": \"deepwiki\" }, { \"type\": \"image_generation\" } - ]. Required.""" + ]""" @overload def __init__( diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py index 64f92690e19f..8fd648a139e3 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py @@ -7535,29 +7535,23 @@ def get_session_log_stream( Each SSE frame contains: * `event`: always `"log"` - * `data`: a plain-text log line (currently JSON-formatted, but the schema - is not contractual and may include additional keys or change format - over time — clients should treat it as an opaque string) + * `data`: a plain-text log line (currently JSON-formatted, but the schema is not contractual and may include additional keys or change format over time; clients should treat it as an opaque string) Example SSE frames: .. code-block:: event: log - data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting - FoundryCBAgent server on port 8088"} + data: {"timestamp":"2026-03-10T09:33:17.121Z","stream":"stdout","message":"Starting FoundryCBAgent server on port 8088"} event: log - data: {"timestamp":"2026-03-10T09:33:17.130Z","stream":"stderr","message":"INFO: Application - startup complete."} + data: {"timestamp":"2026-03-10T09:33:17.130Z","stream":"stderr","message":"INFO: Application startup complete."} event: log - data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully - connected to container"} + data: {"timestamp":"2026-03-10T09:34:52.714Z","stream":"status","message":"Successfully connected to container"} event: log - data: {"timestamp":"2026-03-10T09:35:52.714Z","stream":"status","message":"No logs since - last 60 seconds"} + data: {"timestamp":"2026-03-10T09:35:52.714Z","stream":"status","message":"No logs since last 60 seconds"} The stream remains open until the client disconnects or the server terminates the connection. Clients should handle reconnection as needed. @@ -7599,7 +7593,7 @@ def get_session_log_stream( _request.url = self._client.format_url(_request.url, **path_format_arguments) _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) + _stream = True pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) diff --git a/sdk/ai/azure-ai-projects/tests/samples/llm_instructions.py b/sdk/ai/azure-ai-projects/tests/samples/llm_instructions.py index 0e0f776441cf..af98d794a98d 100644 --- a/sdk/ai/azure-ai-projects/tests/samples/llm_instructions.py +++ b/sdk/ai/azure-ai-projects/tests/samples/llm_instructions.py @@ -18,8 +18,7 @@ from typing import Final -agent_tools_instructions: Final[str] = ( - """ +agent_tools_instructions: Final[str] = """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct for a tool-driven assistant workflow. @@ -44,11 +43,9 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() -) -memories_instructions: Final[str] = ( - """ +memories_instructions: Final[str] = """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct for a memories workflow. @@ -73,11 +70,9 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() -) -agents_instructions: Final[str] = ( - """ +agents_instructions: Final[str] = """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct. @@ -108,11 +103,9 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() -) -chat_completions_instructions: Final[str] = ( - """ +chat_completions_instructions: Final[str] = """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct for Chat Completions scenarios. @@ -131,11 +124,9 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() -) -resource_management_instructions: Final[str] = ( - """ +resource_management_instructions: Final[str] = """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct for resource-management samples (for example connections, files, and deployments). @@ -161,11 +152,9 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() -) -fine_tuning_instructions: Final[str] = ( - """ +fine_tuning_instructions: Final[str] = """ We just ran Python code and captured print/log output in an attached log file (TXT). Validate whether sample execution/output is correct for a fine-tuning workflow. @@ -189,11 +178,9 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() -) -evaluations_instructions: Final[str] = ( - """ +evaluations_instructions: Final[str] = """ We just ran Python code for an evaluation sample and captured print/log output in an attached log file (TXT). Your job: determine if the sample code executed to completion WITHOUT throwing an unhandled exception. @@ -215,11 +202,9 @@ Always respond with `reason` indicating the reason for the response. """.strip() -) -hosted_agents_instructions: Final[str] = ( - """ +hosted_agents_instructions: Final[str] = """ We just ran Python code for a hosted-agent sample and captured print/log output in an attached log file (TXT). Validate whether the sample executed correctly. @@ -241,7 +226,6 @@ Always include `reason` with a concise explanation tied to the observed print output. """.strip() -) # Folder (under samples/) -> instructions. From 1694b45237bee07fc1d8326ac0b76b5a28a8df75 Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Tue, 16 Jun 2026 07:43:17 -0700 Subject: [PATCH 6/9] Restore pyproject.toml --- sdk/ai/azure-ai-projects/pyproject.toml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/sdk/ai/azure-ai-projects/pyproject.toml b/sdk/ai/azure-ai-projects/pyproject.toml index 6524161e8da2..26eac48e1123 100644 --- a/sdk/ai/azure-ai-projects/pyproject.toml +++ b/sdk/ai/azure-ai-projects/pyproject.toml @@ -14,20 +14,21 @@ name = "azure-ai-projects" authors = [ { name = "Microsoft Corporation", email = "azpysdkhelp@microsoft.com" }, ] -description = "Microsoft Corporation Azure Ai Projects Client Library for Python" +description = "Microsoft Corporation Azure AI Projects Client Library for Python" license = "MIT" classifiers = [ - "Development Status :: 4 - Beta", + "Development Status :: 5 - Production/Stable", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", ] -requires-python = ">=3.10" +requires-python = ">=3.9" keywords = ["azure", "azure sdk"] dependencies = [ @@ -43,7 +44,7 @@ dynamic = [ ] [project.urls] -repository = "https://github.com/Azure/azure-sdk-for-python" +repository = "https://aka.ms/azsdk/azure-ai-projects-v2/python/code" [tool.setuptools.dynamic] version = {attr = "azure.ai.projects._version.VERSION"} @@ -51,13 +52,13 @@ readme = {file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown"} [tool.setuptools.packages.find] exclude = [ - "tests*", + "azure.ai", + "azure", + "doc*", + "generated_samples*", "generated_tests*", "samples*", - "generated_samples*", - "doc*", - "azure", - "azure.ai", + "tests*", ] [tool.setuptools.package-data] @@ -68,3 +69,4 @@ verifytypes = false [tool.azure-sdk-conda] in_bundle = false + From bf7da4805ab8093cafa73c2530fb71fae1a4f8db Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Tue, 16 Jun 2026 08:30:28 -0700 Subject: [PATCH 7/9] Add comments --- sdk/ai/azure-ai-projects/PostEmitter.ps1 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sdk/ai/azure-ai-projects/PostEmitter.ps1 b/sdk/ai/azure-ai-projects/PostEmitter.ps1 index bba7b1714319..a3a4f7ce0c4c 100644 --- a/sdk/ai/azure-ai-projects/PostEmitter.ps1 +++ b/sdk/ai/azure-ai-projects/PostEmitter.ps1 @@ -113,6 +113,19 @@ foreach ($f in $files) { Set-Content $f $c -NoNewline } +# A block of code in the implementation of "list_memories", in both sync +# and async _operations.py files, needs to be moved up. It's emitted in the wrong place, +# in the inline function named "prepare_request". Instead it should be moved up into the +# main body of the "list_memories" method, just before the line "error_map.update(kwargs.pop("error_map", {}) or {})". +# If you don't do this, the PR pipeline will show failures in Pyright (`error: "body" is unbound (reportUnboundVariable)`) +# and some tests will fail. This is the block of code that needs to move up: +# if body is _Unset: +# if scope is _Unset: +# raise TypeError("missing required argument: scope") +# body = {"scope": scope} +# body = {k: v for k, v in body.items() if v is not None} + + # Finishing by running 'black' tool to format code. pip install black black --config ../../../eng/black-pyproject.toml . From a2d49d27a6aca8966062b41f5fcaad40bd2c6244 Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Tue, 16 Jun 2026 08:41:47 -0700 Subject: [PATCH 8/9] Add more fix in the script --- sdk/ai/azure-ai-projects/PostEmitter.ps1 | 64 ++++++++++++++++++- .../ai/projects/aio/operations/_operations.py | 11 ++-- .../ai/projects/operations/_operations.py | 11 ++-- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/sdk/ai/azure-ai-projects/PostEmitter.ps1 b/sdk/ai/azure-ai-projects/PostEmitter.ps1 index a3a4f7ce0c4c..cac7cf0aa873 100644 --- a/sdk/ai/azure-ai-projects/PostEmitter.ps1 +++ b/sdk/ai/azure-ai-projects/PostEmitter.ps1 @@ -116,7 +116,7 @@ foreach ($f in $files) { # A block of code in the implementation of "list_memories", in both sync # and async _operations.py files, needs to be moved up. It's emitted in the wrong place, # in the inline function named "prepare_request". Instead it should be moved up into the -# main body of the "list_memories" method, just before the line "error_map.update(kwargs.pop("error_map", {}) or {})". +# main body of the "list_memories" method, right after the line `error_map.update(kwargs.pop("error_map", {}) or {})`. # If you don't do this, the PR pipeline will show failures in Pyright (`error: "body" is unbound (reportUnboundVariable)`) # and some tests will fail. This is the block of code that needs to move up: # if body is _Unset: @@ -124,6 +124,68 @@ foreach ($f in $files) { # raise TypeError("missing required argument: scope") # body = {"scope": scope} # body = {k: v for k, v in body.items() if v is not None} +# The block inside prepare_request has 12-space indentation; after moving to the main function body it needs 8-space indentation. +# Strategy: Find the last list_memories method, then do a targeted string replacement that moves the block right after error_map.update. +$oldPattern = @" + error_map.update(kwargs.pop("error_map", {}) or {}) + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + def prepare_request(_continuation_token=None): + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = {"scope": scope} + body = {k: v for k, v in body.items() if v is not None} + + _request = build_beta_memory_stores_list_memories_request( +"@ +$newPattern = @" + error_map.update(kwargs.pop("error_map", {}) or {}) + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = {"scope": scope} + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + def prepare_request(_continuation_token=None): + _request = build_beta_memory_stores_list_memories_request( +"@ +$files = 'azure\ai\projects\operations\_operations.py', 'azure\ai\projects\aio\operations\_operations.py' +foreach ($f in $files) { + $c = Get-Content $f -Raw + # Find all occurrences of "def list_memories(" and get the index of the last one + $methodMatches = [regex]::Matches($c, 'def list_memories\(') + if ($methodMatches.Count -eq 0) { continue } + $lastMethodStart = $methodMatches[$methodMatches.Count - 1].Index + + # Find the pattern to replace - first occurrence after the last list_memories method + $patternEscaped = [regex]::Escape($oldPattern) + $patternMatches = [regex]::Matches($c, $patternEscaped) + $matchToReplace = $null + foreach ($m in $patternMatches) { + if ($m.Index -gt $lastMethodStart) { + $matchToReplace = $m + break + } + } + if ($matchToReplace -eq $null) { continue } + + # Replace only that specific occurrence + $c = $c.Substring(0, $matchToReplace.Index) + $newPattern + $c.Substring($matchToReplace.Index + $matchToReplace.Length) + + Set-Content $f $c -NoNewline +} # Finishing by running 'black' tool to format code. diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py index 8274ba529518..6d72581d3585 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py @@ -9013,6 +9013,11 @@ def list_memories( 304: ResourceNotModifiedError, } error_map.update(kwargs.pop("error_map", {}) or {}) + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = {"scope": scope} + body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None if isinstance(body, (IOBase, bytes)): @@ -9021,12 +9026,6 @@ def list_memories( _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore def prepare_request(_continuation_token=None): - if body is _Unset: - if scope is _Unset: - raise TypeError("missing required argument: scope") - body = {"scope": scope} - body = {k: v for k, v in body.items() if v is not None} - _request = build_beta_memory_stores_list_memories_request( name=name, kind=kind, diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py index 8fd648a139e3..2ca2deb79964 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py @@ -12436,6 +12436,11 @@ def list_memories( 304: ResourceNotModifiedError, } error_map.update(kwargs.pop("error_map", {}) or {}) + if body is _Unset: + if scope is _Unset: + raise TypeError("missing required argument: scope") + body = {"scope": scope} + body = {k: v for k, v in body.items() if v is not None} content_type = content_type or "application/json" _content = None if isinstance(body, (IOBase, bytes)): @@ -12444,12 +12449,6 @@ def list_memories( _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore def prepare_request(_continuation_token=None): - if body is _Unset: - if scope is _Unset: - raise TypeError("missing required argument: scope") - body = {"scope": scope} - body = {k: v for k, v in body.items() if v is not None} - _request = build_beta_memory_stores_list_memories_request( name=name, kind=kind, From 618d1c6dc9a3d27926dc440e71ee2cfc07b91c53 Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:33:25 -0700 Subject: [PATCH 9/9] Update api.md --- sdk/ai/azure-ai-projects/api.md | 4 ++-- sdk/ai/azure-ai-projects/api.metadata.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/ai/azure-ai-projects/api.md b/sdk/ai/azure-ai-projects/api.md index 657edf783e06..aa20e54a46e6 100644 --- a/sdk/ai/azure-ai-projects/api.md +++ b/sdk/ai/azure-ai-projects/api.md @@ -6954,14 +6954,14 @@ namespace azure.ai.projects.models class azure.ai.projects.models.OptimizationInlineDatasetInput(OptimizationDatasetInput, discriminator='inline'): - items_property: list[OptimizationDatasetItem] + dataset_items: list[OptimizationDatasetItem] type: Literal[OptimizationDatasetInputType.INLINE] @overload def __init__( self, *, - items_property: list[OptimizationDatasetItem] + dataset_items: list[OptimizationDatasetItem] ) -> None: ... @overload diff --git a/sdk/ai/azure-ai-projects/api.metadata.yml b/sdk/ai/azure-ai-projects/api.metadata.yml index f930bb0f724f..881bafc80613 100644 --- a/sdk/ai/azure-ai-projects/api.metadata.yml +++ b/sdk/ai/azure-ai-projects/api.metadata.yml @@ -1,3 +1,3 @@ -apiMdSha256: 21533487ff2217a30e5e9e61194466340ea4be034aef29cc66784996eaa298d0 +apiMdSha256: f4c05c4124d4d6f1d59ec55aefc17f8184427317294f3a526d8d98d6133c35d8 parserVersion: 0.3.28 pythonVersion: 3.14.3