From 2934b1d85f23575adade052d282d18c765a618ba Mon Sep 17 00:00:00 2001 From: Gloria Huang <179177515+gloriahxr@users.noreply.github.com> Date: Fri, 14 Aug 2026 02:23:13 -0700 Subject: [PATCH 1/3] [AI Manager] Add az aimanager modelsource commands Add the 'az aimanager modelsource' command group, backed by the ModelSources operations of the vendored 2026-05-02-preview SDK, against .../aiManagers/{aiManagerName}/modelSources[/{modelSourceName}]: * az aimanager modelsource add|update|show|list|delete (+ wait) 'add' requires --source-type/-s (currently HuggingFace) and optionally takes --description and --token, where --token is sent as an inline credential for gated or private sources. 'update' is a read-modify-PUT that preserves the immutable source type and any omitted description. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/aimanager/HISTORY.rst | 5 + .../azext_aimanager/_client_factory.py | 4 + src/aimanager/azext_aimanager/_help.py | 61 ++++++++++ src/aimanager/azext_aimanager/_params.py | 33 +++++- src/aimanager/azext_aimanager/_validators.py | 6 + .../azext_aimanager/azext_metadata.json | 2 +- src/aimanager/azext_aimanager/commands.py | 16 +++ src/aimanager/azext_aimanager/constants.py | 3 + src/aimanager/azext_aimanager/custom.py | 102 ++++++++++++++++ .../tests/latest/test_modelsource.py | 112 ++++++++++++++++++ .../tests/latest/test_modelsource_scenario.py | 74 ++++++++++++ src/aimanager/setup.py | 2 +- 12 files changed, 417 insertions(+), 3 deletions(-) create mode 100644 src/aimanager/azext_aimanager/tests/latest/test_modelsource.py create mode 100644 src/aimanager/azext_aimanager/tests/latest/test_modelsource_scenario.py diff --git a/src/aimanager/HISTORY.rst b/src/aimanager/HISTORY.rst index f3812405cfd..9ae87c0db8c 100644 --- a/src/aimanager/HISTORY.rst +++ b/src/aimanager/HISTORY.rst @@ -3,6 +3,11 @@ Release History =============== +1.2.1b1 ++++++++ +* Add ``az aimanager modelsource`` commands to add, update, list, show, delete, and wait for + model sources. + 1.2.0 ++++++ * Add ``az aimanager namespace modeldeployment`` commands to add, update, list, show, delete, diff --git a/src/aimanager/azext_aimanager/_client_factory.py b/src/aimanager/azext_aimanager/_client_factory.py index 4a6351168ef..2970a112176 100644 --- a/src/aimanager/azext_aimanager/_client_factory.py +++ b/src/aimanager/azext_aimanager/_client_factory.py @@ -25,3 +25,7 @@ def cf_ai_manager_namespaces(cli_ctx, *_): def cf_model_deployments(cli_ctx, *_): return get_aimanager_client(cli_ctx).model_deployments + + +def cf_model_sources(cli_ctx, *_): + return get_aimanager_client(cli_ctx).model_sources diff --git a/src/aimanager/azext_aimanager/_help.py b/src/aimanager/azext_aimanager/_help.py index 4964224cc91..122fdec2a22 100644 --- a/src/aimanager/azext_aimanager/_help.py +++ b/src/aimanager/azext_aimanager/_help.py @@ -76,6 +76,67 @@ text: az aimanager get-credentials --name my-ai-manager -g myrg -f - """ +helps['aimanager modelsource'] = """ + type: group + short-summary: Manage model sources within an AI Manager. + long-summary: |- + A model source tells the platform where to pull model artifacts from and, for gated or + private sources, which credential to authenticate with. Model sources are referenced by + 'az aimanager namespace modeldeployment add --model-source-resource-id'. +""" + +helps['aimanager modelsource add'] = """ + type: command + short-summary: Add a model source to an AI Manager. + examples: + - name: Add a public Hugging Face model source + text: az aimanager modelsource add -g myrg --aimanager-name my-ai-manager -n hf --source-type HuggingFace + - name: Add a Hugging Face model source with an access token for gated models + text: az aimanager modelsource add -g myrg --aimanager-name my-ai-manager -n hf -s HuggingFace --token hf_xxx --description "Gated models" +""" + +helps['aimanager modelsource update'] = """ + type: command + short-summary: Update a model source within an AI Manager. + long-summary: |- + The source type is immutable after creation and is always preserved. Omitted properties + keep their current values. + examples: + - name: Rotate the access token of a model source + text: az aimanager modelsource update -g myrg --aimanager-name my-ai-manager -n hf --token hf_yyy + - name: Update the description of a model source + text: az aimanager modelsource update -g myrg --aimanager-name my-ai-manager -n hf --description "Internal mirror" +""" + +helps['aimanager modelsource show'] = """ + type: command + short-summary: Show the details of a model source within an AI Manager. + examples: + - name: Show a model source + text: az aimanager modelsource show -g myrg --aimanager-name my-ai-manager -n hf +""" + +helps['aimanager modelsource list'] = """ + type: command + short-summary: List the model sources within an AI Manager. + examples: + - name: List model sources + text: az aimanager modelsource list -g myrg --aimanager-name my-ai-manager +""" + +helps['aimanager modelsource delete'] = """ + type: command + short-summary: Delete a model source from an AI Manager. + examples: + - name: Delete a model source + text: az aimanager modelsource delete -g myrg --aimanager-name my-ai-manager -n hf +""" + +helps['aimanager modelsource wait'] = """ + type: command + short-summary: Wait for an AI Manager model source to reach a desired state. +""" + helps['aimanager namespace'] = """ type: group short-summary: Manage namespaces within an AI Manager. diff --git a/src/aimanager/azext_aimanager/_params.py b/src/aimanager/azext_aimanager/_params.py index b859b8577de..3fb28d6a716 100644 --- a/src/aimanager/azext_aimanager/_params.py +++ b/src/aimanager/azext_aimanager/_params.py @@ -10,11 +10,16 @@ get_resource_name_completion_list, ) from azure.cli.core.commands.validators import get_default_location_from_resource_group -from azext_aimanager.constants import DELETE_POLICIES, MODEL_DEPLOYMENT_PERFORMANCE_MODES +from azext_aimanager.constants import ( + DELETE_POLICIES, + MODEL_DEPLOYMENT_PERFORMANCE_MODES, + MODEL_SOURCE_TYPES, +) from azext_aimanager._validators import ( validate_ai_manager_name, validate_namespace_name, validate_model_deployment_name, + validate_model_source_name, validate_labels, validate_annotations, validate_overrides, @@ -53,6 +58,32 @@ def load_arguments(self, _): c.argument('aks_custom_headers', options_list=['--aks-custom-headers'], help='Comma-separated key=value pairs to specify custom headers.') + with self.argument_context('aimanager modelsource') as c: + c.argument('ai_manager_name', options_list=['--aimanager-name'], + validator=validate_ai_manager_name, + help='The name of the AI Manager resource.') + c.argument('model_source_name', options_list=['--name', '-n'], + validator=validate_model_source_name, + help='The name of the model source.') + + with self.argument_context('aimanager modelsource list') as c: + c.ignore('model_source_name') + + with self.argument_context('aimanager modelsource add') as c: + c.argument('source_type', options_list=['--source-type', '-s'], required=True, + arg_type=get_enum_type(MODEL_SOURCE_TYPES), + help='The type of the model source. Immutable after creation.') + + for scope in ['aimanager modelsource add', 'aimanager modelsource update']: + with self.argument_context(scope) as c: + c.argument('description', + help='An optional, free-form description of the model source.') + c.argument('token', + help='Access token used by the platform to authenticate to the source. ' + 'Optional for public sources such as ungated Hugging Face models.') + c.argument('aks_custom_headers', options_list=['--aks-custom-headers'], + help='Comma-separated key=value pairs to specify custom headers.') + with self.argument_context('aimanager namespace') as c: c.argument('ai_manager_name', options_list=['--manager', '-m'], validator=validate_ai_manager_name, diff --git a/src/aimanager/azext_aimanager/_validators.py b/src/aimanager/azext_aimanager/_validators.py index b7423de3a1e..7ef1e061ef8 100644 --- a/src/aimanager/azext_aimanager/_validators.py +++ b/src/aimanager/azext_aimanager/_validators.py @@ -22,6 +22,12 @@ def validate_model_deployment_name(namespace): raise InvalidArgumentValueError("--name/-n is not a valid model deployment name.") +def validate_model_source_name(namespace): + name = getattr(namespace, "model_source_name", None) + if name is not None and not name.strip(): + raise InvalidArgumentValueError("--name/-n is not a valid model source name.") + + def _validate_key_value_pairs(values, option): if not values: return diff --git a/src/aimanager/azext_aimanager/azext_metadata.json b/src/aimanager/azext_aimanager/azext_metadata.json index 8ad409021bd..bda33e00008 100644 --- a/src/aimanager/azext_aimanager/azext_metadata.json +++ b/src/aimanager/azext_aimanager/azext_metadata.json @@ -1,5 +1,5 @@ { "azext.isPreview": true, "azext.minCliCoreVersion": "2.61.0", - "version": "1.2.0" + "version": "1.2.1b1" } diff --git a/src/aimanager/azext_aimanager/commands.py b/src/aimanager/azext_aimanager/commands.py index d100c263b27..260d03ae19b 100644 --- a/src/aimanager/azext_aimanager/commands.py +++ b/src/aimanager/azext_aimanager/commands.py @@ -9,6 +9,7 @@ cf_ai_managers, cf_ai_manager_namespaces, cf_model_deployments, + cf_model_sources, ) @@ -52,6 +53,21 @@ def load_command_table(self, _): g.custom_command("get-credentials", "aimanager_namespace_get_credentials") g.wait_command("wait") + model_sources_sdk = CliCommandType( + operations_tmpl="azext_aimanager.vendored_sdks.v2026_05_02_preview.operations._operations#ModelSourcesOperations.{}", + operation_group="model_sources", + client_factory=cf_model_sources + ) + + # aimanager modelsource command group + with self.command_group("aimanager modelsource", model_sources_sdk, client_factory=cf_model_sources) as g: + g.custom_command("add", "add_modelsource", supports_no_wait=True) + g.custom_command("update", "update_modelsource", supports_no_wait=True) + g.custom_show_command("show", "show_modelsource") + g.custom_command("list", "list_modelsource") + g.custom_command("delete", "delete_modelsource", supports_no_wait=True, confirmation=True) + g.custom_wait_command("wait", "show_modelsource") + # aimanager namespace modeldeployment command group with self.command_group("aimanager namespace modeldeployment", model_deployments_sdk, client_factory=cf_model_deployments) as g: diff --git a/src/aimanager/azext_aimanager/constants.py b/src/aimanager/azext_aimanager/constants.py index edebdcfabfe..ffe259c1bc6 100644 --- a/src/aimanager/azext_aimanager/constants.py +++ b/src/aimanager/azext_aimanager/constants.py @@ -9,3 +9,6 @@ DELETE_POLICIES = [DELETE_POLICY_KEEP, DELETE_POLICY_DELETE] MODEL_DEPLOYMENT_PERFORMANCE_MODES = ["Balanced", "Latency", "Throughput"] + +# Supported model source types for an AI Manager model source. +MODEL_SOURCE_TYPES = ["HuggingFace"] diff --git a/src/aimanager/azext_aimanager/custom.py b/src/aimanager/azext_aimanager/custom.py index 6da27d25606..6b3a5cf878a 100644 --- a/src/aimanager/azext_aimanager/custom.py +++ b/src/aimanager/azext_aimanager/custom.py @@ -302,6 +302,108 @@ def aimanager_namespace_get_credentials(cmd, # endregion +# region Model source + +def _construct_modelsource(cmd, source_type, description=None, token=None): + properties_model = _get_model(cmd, "ModelSourceProperties", "model_sources") + source_model = _get_model(cmd, "ModelSource", "model_sources") + + credential = None + if token is not None: + credential_model = _get_model(cmd, "CredentialValue", "model_sources") + inline_model = _get_model(cmd, "InlineCredential", "model_sources") + credential = credential_model(inline=inline_model(value=token)) + + return source_model(properties=properties_model( + source_type=source_type, + description=description, + credential=credential, + )) + + +# pylint: disable=unused-argument +def add_modelsource(cmd, + client, + resource_group_name, + ai_manager_name, + model_source_name, + source_type, + description=None, + token=None, + aks_custom_headers=None, + no_wait=False): + try: + client.get(resource_group_name, ai_manager_name, model_source_name) + except ResourceNotFoundError: + pass + else: + raise ClientRequestError( + f"Model source '{model_source_name}' already exists. " + "Please use 'az aimanager modelsource update' to update it.") + + model_source = _construct_modelsource(cmd, source_type, description, token) + headers = get_aks_custom_headers(aks_custom_headers) + return sdk_no_wait( + no_wait, client.begin_create_or_update, resource_group_name, ai_manager_name, + model_source_name, model_source, headers=headers) + + +# pylint: disable=unused-argument +def update_modelsource(cmd, + client, + resource_group_name, + ai_manager_name, + model_source_name, + description=None, + token=None, + aks_custom_headers=None, + no_wait=False): + try: + existing = client.get(resource_group_name, ai_manager_name, model_source_name) + except ResourceNotFoundError: + raise ClientRequestError( + f"Model source '{model_source_name}' doesn't exist. " + "Please use 'az aimanager modelsource list' to get the current list of model sources.") + + existing_properties = existing.properties + if description is None and existing_properties is not None: + description = existing_properties.description + # sourceType is immutable after creation, so it is always carried over from the existing + # resource on this create-or-replace PUT. + source_type = existing_properties.source_type if existing_properties is not None else None + + model_source = _construct_modelsource(cmd, source_type, description, token) + headers = get_aks_custom_headers(aks_custom_headers) + return sdk_no_wait( + no_wait, client.begin_create_or_update, resource_group_name, ai_manager_name, + model_source_name, model_source, headers=headers) + + +def show_modelsource(cmd, client, resource_group_name, ai_manager_name, + model_source_name): # pylint: disable=unused-argument + return client.get(resource_group_name, ai_manager_name, model_source_name) + + +def list_modelsource(cmd, client, resource_group_name, + ai_manager_name): # pylint: disable=unused-argument + return client.list(resource_group_name, ai_manager_name) + + +def delete_modelsource(cmd, client, resource_group_name, ai_manager_name, model_source_name, + no_wait=False): # pylint: disable=unused-argument + try: + client.get(resource_group_name, ai_manager_name, model_source_name) + except ResourceNotFoundError: + raise ClientRequestError( + f"Model source '{model_source_name}' doesn't exist. " + "Please use 'az aimanager modelsource list' to get the current list of model sources.") + + return sdk_no_wait( + no_wait, client.begin_delete, resource_group_name, ai_manager_name, model_source_name) + +# endregion + + # region Model deployment def _construct_scaling_profile(cmd, replicas=None, min_replicas=None, max_replicas=None, diff --git a/src/aimanager/azext_aimanager/tests/latest/test_modelsource.py b/src/aimanager/azext_aimanager/tests/latest/test_modelsource.py new file mode 100644 index 00000000000..d948f02cee9 --- /dev/null +++ b/src/aimanager/azext_aimanager/tests/latest/test_modelsource.py @@ -0,0 +1,112 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import unittest +from types import SimpleNamespace +from unittest.mock import MagicMock, patch + +from azure.cli.core.azclierror import ClientRequestError, InvalidArgumentValueError +from azure.core.exceptions import ResourceNotFoundError + +from azext_aimanager import custom +from azext_aimanager._validators import validate_model_source_name +from azext_aimanager.vendored_sdks.v2026_05_02_preview import models + + +class MockCmd: + def get_models(self, name, **_): + return getattr(models, name) + + +class TestModelSource(unittest.TestCase): + + def setUp(self): + self.cmd = MockCmd() + self.client = MagicMock() + + def test_construct_without_token_omits_credential(self): + source = custom._construct_modelsource(self.cmd, "HuggingFace", "desc") + + self.assertEqual(source.properties.source_type, "HuggingFace") + self.assertEqual(source.properties.description, "desc") + self.assertIsNone(source.properties.credential) + + def test_construct_with_token_sets_inline_credential(self): + source = custom._construct_modelsource(self.cmd, "HuggingFace", None, "hf_token") + + self.assertEqual(source.properties.credential.inline.value, "hf_token") + + def test_add_rejects_existing_source(self): + self.client.get.return_value = object() + + with self.assertRaises(ClientRequestError): + custom.add_modelsource( + self.cmd, self.client, "rg", "manager", "source", "HuggingFace") + + def test_update_rejects_missing_source(self): + self.client.get.side_effect = ResourceNotFoundError() + + with self.assertRaises(ClientRequestError): + custom.update_modelsource(self.cmd, self.client, "rg", "manager", "source") + + @patch.object(custom, "sdk_no_wait") + @patch.object(custom, "_construct_modelsource") + def test_update_preserves_source_type_and_description( + self, construct_modelsource, sdk_no_wait): + self.client.get.return_value = SimpleNamespace( + properties=SimpleNamespace( + source_type="HuggingFace", + description="existing description", + )) + construct_modelsource.return_value = "model-source" + sdk_no_wait.return_value = "result" + + result = custom.update_modelsource( + self.cmd, self.client, "rg", "manager", "source", token="hf_new") + + self.assertEqual(result, "result") + construct_modelsource.assert_called_once_with( + self.cmd, "HuggingFace", "existing description", "hf_new") + sdk_no_wait.assert_called_once_with( + False, + self.client.begin_create_or_update, + "rg", + "manager", + "source", + "model-source", + headers={}, + ) + + def test_delete_rejects_missing_source(self): + self.client.get.side_effect = ResourceNotFoundError() + + with self.assertRaises(ClientRequestError): + custom.delete_modelsource(self.cmd, self.client, "rg", "manager", "source") + + def test_list_uses_ai_manager_scope(self): + self.client.list.return_value = ["source"] + + result = custom.list_modelsource(self.cmd, self.client, "rg", "manager") + + self.assertEqual(result, ["source"]) + self.client.list.assert_called_once_with("rg", "manager") + + +class TestModelSourceValidators(unittest.TestCase): + + def test_valid_name(self): + validate_model_source_name(SimpleNamespace(model_source_name="hf")) + + def test_missing_name_is_allowed(self): + validate_model_source_name(SimpleNamespace(model_source_name=None)) + validate_model_source_name(SimpleNamespace()) + + def test_blank_name_is_rejected(self): + with self.assertRaises(InvalidArgumentValueError): + validate_model_source_name(SimpleNamespace(model_source_name=" ")) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/aimanager/azext_aimanager/tests/latest/test_modelsource_scenario.py b/src/aimanager/azext_aimanager/tests/latest/test_modelsource_scenario.py new file mode 100644 index 00000000000..12ec84082af --- /dev/null +++ b/src/aimanager/azext_aimanager/tests/latest/test_modelsource_scenario.py @@ -0,0 +1,74 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from unittest.mock import MagicMock, patch + +from azure.cli.testsdk import ScenarioTest +from azure.core.exceptions import ResourceNotFoundError + +from azext_aimanager.vendored_sdks.v2026_05_02_preview import models + + +class ModelSourceScenarioTest(ScenarioTest): + + def test_modelsource_commands(self): + model_source = models.ModelSource({ + 'name': 'hf', + 'properties': { + 'sourceType': 'HuggingFace', + 'description': 'Hugging Face registry', + 'provisioningState': 'Succeeded', + }, + }) + + operations = MagicMock() + operations.get.side_effect = [ + ResourceNotFoundError(), + model_source, + model_source, + model_source, + model_source, + ] + operations.list.return_value = [model_source] + service_client = MagicMock() + service_client.model_sources = operations + + command_prefix = 'aimanager modelsource {} -g rg --aimanager-name manager' + + with patch('azext_aimanager._client_factory.get_aimanager_client', + return_value=service_client): + self.cmd( + command_prefix.format('add') + + ' -n hf -s HuggingFace --token hf_xxx --description "Hugging Face registry" --no-wait', + checks=[self.is_empty()]) + + self.cmd( + command_prefix.format('show') + ' -n hf', + checks=[ + self.check('name', 'hf'), + self.check('properties.sourceType', 'HuggingFace'), + self.check('properties.description', 'Hugging Face registry'), + ]) + + self.cmd( + command_prefix.format('list'), + checks=[self.check("length([?name=='hf'])", 1)]) + + self.cmd( + command_prefix.format('update') + ' -n hf --token hf_yyy --no-wait', + checks=[self.is_empty()]) + + self.cmd( + command_prefix.format('delete') + ' -n hf --yes --no-wait', + checks=[self.is_empty()]) + + self.assertEqual(operations.begin_create_or_update.call_count, 2) + operations.list.assert_called_once_with('rg', 'manager') + operations.begin_delete.assert_called_once() + + # the source type is immutable and must be carried over on update + update_payload = operations.begin_create_or_update.call_args_list[1][0][3] + self.assertEqual(update_payload.properties.source_type, 'HuggingFace') + self.assertEqual(update_payload.properties.credential.inline.value, 'hf_yyy') diff --git a/src/aimanager/setup.py b/src/aimanager/setup.py index 399850f3d04..11bac3528e6 100644 --- a/src/aimanager/setup.py +++ b/src/aimanager/setup.py @@ -14,7 +14,7 @@ from distutils import log as logger logger.warn("Wheel is not available, disabling bdist_wheel hook") -VERSION = '1.2.0' +VERSION = '1.2.1b1' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers From d89403ccbac70e6a7ef1c80873694ec2aee7c048 Mon Sep 17 00:00:00 2001 From: Gloria Huang <179177515+gloriahxr@users.noreply.github.com> Date: Fri, 14 Aug 2026 03:06:57 -0700 Subject: [PATCH 2/3] Tighten modelsource scenario test and reword HISTORY entry Match the get side effect list to the four reads the scenario actually makes and assert the call count, so an unexpected extra GET fails the test. Also reword the HISTORY entry to read as a complete phrase. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/aimanager/HISTORY.rst | 4 ++-- .../azext_aimanager/tests/latest/test_modelsource_scenario.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/aimanager/HISTORY.rst b/src/aimanager/HISTORY.rst index 9ae87c0db8c..05ed70a44ab 100644 --- a/src/aimanager/HISTORY.rst +++ b/src/aimanager/HISTORY.rst @@ -5,8 +5,8 @@ Release History 1.2.1b1 +++++++ -* Add ``az aimanager modelsource`` commands to add, update, list, show, delete, and wait for - model sources. +* Add ``az aimanager modelsource`` commands (``add``, ``update``, ``list``, ``show``, + ``delete`` and ``wait``) to manage the model sources of an AI Manager. 1.2.0 ++++++ diff --git a/src/aimanager/azext_aimanager/tests/latest/test_modelsource_scenario.py b/src/aimanager/azext_aimanager/tests/latest/test_modelsource_scenario.py index 12ec84082af..8cb2d0c8fdf 100644 --- a/src/aimanager/azext_aimanager/tests/latest/test_modelsource_scenario.py +++ b/src/aimanager/azext_aimanager/tests/latest/test_modelsource_scenario.py @@ -25,11 +25,11 @@ def test_modelsource_commands(self): operations = MagicMock() operations.get.side_effect = [ + # add -> not found, then show / update / delete each read the resource once ResourceNotFoundError(), model_source, model_source, model_source, - model_source, ] operations.list.return_value = [model_source] service_client = MagicMock() @@ -64,6 +64,7 @@ def test_modelsource_commands(self): command_prefix.format('delete') + ' -n hf --yes --no-wait', checks=[self.is_empty()]) + self.assertEqual(operations.get.call_count, 4) self.assertEqual(operations.begin_create_or_update.call_count, 2) operations.list.assert_called_once_with('rg', 'manager') operations.begin_delete.assert_called_once() From e56489aafd654f167eb6ee542b2275dd9c1dc353 Mon Sep 17 00:00:00 2001 From: Gloria Huang <179177515+gloriahxr@users.noreply.github.com> Date: Sun, 16 Aug 2026 20:46:57 -0700 Subject: [PATCH 3/3] Drop version bump so the release is batched in one PR Per review feedback, keep the version and changelog changes in a single PR (Azure/azure-cli-extensions#10219) so the three related aimanager PRs produce one release instead of three. This PR now carries code only. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/aimanager/HISTORY.rst | 5 ----- src/aimanager/azext_aimanager/azext_metadata.json | 2 +- src/aimanager/setup.py | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/aimanager/HISTORY.rst b/src/aimanager/HISTORY.rst index 05ed70a44ab..f3812405cfd 100644 --- a/src/aimanager/HISTORY.rst +++ b/src/aimanager/HISTORY.rst @@ -3,11 +3,6 @@ Release History =============== -1.2.1b1 -+++++++ -* Add ``az aimanager modelsource`` commands (``add``, ``update``, ``list``, ``show``, - ``delete`` and ``wait``) to manage the model sources of an AI Manager. - 1.2.0 ++++++ * Add ``az aimanager namespace modeldeployment`` commands to add, update, list, show, delete, diff --git a/src/aimanager/azext_aimanager/azext_metadata.json b/src/aimanager/azext_aimanager/azext_metadata.json index bda33e00008..8ad409021bd 100644 --- a/src/aimanager/azext_aimanager/azext_metadata.json +++ b/src/aimanager/azext_aimanager/azext_metadata.json @@ -1,5 +1,5 @@ { "azext.isPreview": true, "azext.minCliCoreVersion": "2.61.0", - "version": "1.2.1b1" + "version": "1.2.0" } diff --git a/src/aimanager/setup.py b/src/aimanager/setup.py index 11bac3528e6..399850f3d04 100644 --- a/src/aimanager/setup.py +++ b/src/aimanager/setup.py @@ -14,7 +14,7 @@ from distutils import log as logger logger.warn("Wheel is not available, disabling bdist_wheel hook") -VERSION = '1.2.1b1' +VERSION = '1.2.0' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers