azureml-registry-tools 0.1.0a11__tar.gz → 0.1.0a13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {azureml_registry_tools-0.1.0a11/azureml_registry_tools.egg-info → azureml_registry_tools-0.1.0a13}/PKG-INFO +1 -1
  2. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/_cli/registry_syndication_cli.py +215 -7
  3. azureml_registry_tools-0.1.0a13/azureml/registry/_rest_client/registry_model_client.py +333 -0
  4. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/data/validate_model_schema.py +49 -11
  5. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/mgmt/asset_management.py +7 -4
  6. azureml_registry_tools-0.1.0a13/azureml/registry/mgmt/model_management.py +169 -0
  7. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13/azureml_registry_tools.egg-info}/PKG-INFO +1 -1
  8. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml_registry_tools.egg-info/SOURCES.txt +2 -0
  9. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/setup.py +1 -1
  10. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/LICENSE.txt +0 -0
  11. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/MANIFEST.in +0 -0
  12. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/__init__.py +0 -0
  13. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/__init__.py +0 -0
  14. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/_cli/__init__.py +0 -0
  15. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/_cli/repo2registry_cli.py +0 -0
  16. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/_rest_client/__init__.py +0 -0
  17. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/_rest_client/arm_client.py +0 -0
  18. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/_rest_client/base_rest_client.py +0 -0
  19. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/_rest_client/registry_management_client.py +0 -0
  20. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/data/__init__.py +0 -0
  21. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/data/asset.yaml.template +0 -0
  22. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/data/description.md.template +0 -0
  23. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/data/evaluation.md.template +0 -0
  24. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/data/model-variant.schema.json +0 -0
  25. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/data/model.schema.json +0 -0
  26. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/data/model.yaml.template +0 -0
  27. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/data/notes.md.template +0 -0
  28. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/data/validate_model_variant_schema.py +0 -0
  29. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/mgmt/__init__.py +0 -0
  30. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/mgmt/create_asset_template.py +0 -0
  31. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/mgmt/create_manifest.py +0 -0
  32. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/mgmt/create_model_spec.py +0 -0
  33. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/mgmt/registry_config.py +0 -0
  34. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/mgmt/syndication_manifest.py +0 -0
  35. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/tools/__init__.py +0 -0
  36. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/tools/config.py +0 -0
  37. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/tools/create_or_update_assets.py +0 -0
  38. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/tools/registry_utils.py +0 -0
  39. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml/registry/tools/repo2registry_config.py +0 -0
  40. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml_registry_tools.egg-info/dependency_links.txt +0 -0
  41. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml_registry_tools.egg-info/entry_points.txt +0 -0
  42. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml_registry_tools.egg-info/requires.txt +0 -0
  43. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/azureml_registry_tools.egg-info/top_level.txt +0 -0
  44. {azureml_registry_tools-0.1.0a11 → azureml_registry_tools-0.1.0a13}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: azureml-registry-tools
3
- Version: 0.1.0a11
3
+ Version: 0.1.0a13
4
4
  Summary: AzureML Registry tools and CLI
5
5
  Author: Microsoft Corp
6
6
  License: https://aka.ms/azureml-sdk-license
@@ -6,13 +6,17 @@ import json
6
6
  import sys
7
7
  from pathlib import Path
8
8
  from uuid import UUID
9
- from azureml.registry._rest_client.registry_management_client import RegistryManagementClient
9
+
10
10
  from azureml.registry._rest_client.arm_client import ArmClient
11
- from azureml.registry.mgmt.asset_management import asset_validate, asset_deploy
11
+ from azureml.registry._rest_client.registry_management_client import RegistryManagementClient
12
+ from azureml.registry.mgmt.asset_management import asset_deploy, asset_validate
12
13
  from azureml.registry.mgmt.create_asset_template import asset_template
13
14
  from azureml.registry.mgmt.create_manifest import generate_syndication_manifest
15
+ from azureml.registry.mgmt.model_management import (
16
+ model_delete, model_get, model_list, parse_asset_id
17
+ )
14
18
  from azureml.registry.mgmt.registry_config import create_registry_config
15
- from azureml.registry.mgmt.syndication_manifest import SyndicationManifest, ResyncAssetsInManifestDto
19
+ from azureml.registry.mgmt.syndication_manifest import ResyncAssetsInManifestDto, SyndicationManifest
16
20
 
17
21
 
18
22
  def syndication_manifest_show(registry_name: str) -> dict:
@@ -195,6 +199,36 @@ def main() -> None:
195
199
  # Create asset template files
196
200
  registry-mgmt asset template --folder ./folder-path
197
201
 
202
+ # List models in registry (auto-discover registry details: subscription/resource-group/primary-region)
203
+ registry-mgmt model list --registry-name myreg
204
+
205
+ # List models with explicit subscription, resource group, and primary region
206
+ registry-mgmt model list --registry-name myreg --subscription sub123 --resource-group rg123 --primary-region eastus
207
+
208
+ # Get a specific model by name and version (auto-discover registry details: subscription/resource-group/primary-region)
209
+ registry-mgmt model get --registry-name myreg --name mymodel --version 1
210
+
211
+ # Get a specific model with explicit subscription, resource group, and primary region
212
+ registry-mgmt model get --registry-name myreg --subscription sub123 --resource-group rg123 --primary-region eastus --name mymodel --version 1
213
+
214
+ # Get a model by asset ID (auto-discover registry details: subscription/resource-group/primary-region)
215
+ registry-mgmt model get --asset-id azureml://registries/myreg/models/mymodel/versions/1
216
+
217
+ # Delete a specific model version (auto-discover registry details: subscription/resource-group/primary-region)
218
+ registry-mgmt model delete --registry-name myreg --name mymodel --version 1
219
+
220
+ # Delete a specific model version with explicit subscription, resource group, and primary region
221
+ registry-mgmt model delete --registry-name myreg --subscription sub123 --resource-group rg123 --primary-region eastus --name mymodel --version 1
222
+
223
+ # Delete a model by asset ID (auto-discover registry details: subscription/resource-group/primary-region)
224
+ registry-mgmt model delete --asset-id azureml://registries/myreg/models/mymodel/versions/1
225
+
226
+ # Delete all versions of a model (auto-discover registry details: subscription/resource-group/primary-region)
227
+ registry-mgmt model delete --registry-name myreg --name mymodel --force
228
+
229
+ # Delete all versions of a model with explicit subscription, resource group, and primary region
230
+ registry-mgmt model delete --registry-name myreg --subscription sub123 --resource-group rg123 --primary-region eastus --name mymodel --force
231
+
198
232
  # Show registry discovery info
199
233
  registry-mgmt show --registry-name myreg
200
234
 
@@ -216,6 +250,52 @@ def main() -> None:
216
250
  }
217
251
  }
218
252
 
253
+ subscription_arg = {
254
+ "args": ("--subscription",),
255
+ "kwargs": {
256
+ "type": str,
257
+ "required": True,
258
+ "help": "Registry subscription ID."
259
+ }
260
+ }
261
+
262
+ resource_group_arg = {
263
+ "args": ("-g", "--resource-group"),
264
+ "kwargs": {
265
+ "type": str,
266
+ "required": True,
267
+ "help": "Registry resource group."
268
+ }
269
+ }
270
+
271
+ # Model-specific arguments (optional for registry auto-discovery)
272
+ model_subscription_arg = {
273
+ "args": ("--subscription",),
274
+ "kwargs": {
275
+ "type": str,
276
+ "required": False,
277
+ "help": "Registry subscription ID. If not provided, auto-resolves from registry discovery."
278
+ }
279
+ }
280
+
281
+ model_resource_group_arg = {
282
+ "args": ("-g", "--resource-group"),
283
+ "kwargs": {
284
+ "type": str,
285
+ "required": False,
286
+ "help": "Registry resource group. If not provided, auto-resolves from registry discovery."
287
+ }
288
+ }
289
+
290
+ model_primary_region_arg = {
291
+ "args": ("--primary-region",),
292
+ "kwargs": {
293
+ "type": str,
294
+ "required": False,
295
+ "help": "Registry primary region. If not provided, auto-resolves from registry discovery."
296
+ }
297
+ }
298
+
219
299
  dry_run_arg = {
220
300
  "args": ("--dry-run",),
221
301
  "kwargs": {
@@ -270,6 +350,8 @@ def main() -> None:
270
350
  asset_validate_parser = asset_subparsers.add_parser("validate", help="Validate an asset.")
271
351
  _add_common_args(asset_validate_parser, [dry_run_arg])
272
352
  asset_validate_parser.add_argument("--asset-path", type=Path, help="Path to the asset folder to validate.")
353
+ asset_validate_parser.add_argument("--allow-additional-properties", action="store_true",
354
+ help="Allow additional properties not defined in the schema")
273
355
 
274
356
  # asset deploy command
275
357
  asset_deploy_parser = asset_subparsers.add_parser("deploy", help="Deploy an asset to registry.")
@@ -279,9 +361,7 @@ def main() -> None:
279
361
 
280
362
  # asset config command
281
363
  asset_config_parser = asset_subparsers.add_parser("config", help="Create registry configuration file.")
282
- asset_config_parser.add_argument("--registry-name", type=str, required=True, help="AzureML Registry name.")
283
- asset_config_parser.add_argument("--subscription", type=str, required=True, help="Registry subscription ID.")
284
- asset_config_parser.add_argument("-g", "--resource-group", type=str, required=True, help="Registry resource group.")
364
+ _add_common_args(asset_config_parser, [registry_name_arg, subscription_arg, resource_group_arg])
285
365
  asset_config_parser.add_argument("--tenant-id", type=str, required=True, help="Registry Tenant ID.")
286
366
  asset_config_parser.add_argument("-c", "--config-file", type=validate_registry_cfg, help="Registry config file path to write to (default: registry-mgmt.cfg).")
287
367
  asset_config_parser.add_argument("--storage-name", type=str, help="Storage account name for storage overrides.")
@@ -293,6 +373,42 @@ def main() -> None:
293
373
  _add_common_args(asset_template_parser, [dry_run_arg])
294
374
  asset_template_parser.add_argument("--folder", type=Path, required=True, help="Path to the folder where asset template files will be created.")
295
375
 
376
+ # model root command
377
+ model_parser = subparsers.add_parser("model", help="Model registry operations")
378
+ model_subparsers = model_parser.add_subparsers(dest="model_subcommand", required=True)
379
+
380
+ # model list command
381
+ model_list_parser = model_subparsers.add_parser("list", help="List models in the registry.")
382
+ _add_common_args(model_list_parser, [registry_name_arg, model_subscription_arg, model_resource_group_arg, model_primary_region_arg])
383
+ model_list_parser.add_argument("--name", type=str, help="Filter by model name.")
384
+ model_list_parser.add_argument("--tags", type=str, help="Comma separated string of tags key or tags key=value.")
385
+ model_list_parser.add_argument("--version", type=str, help="Filter by model version.")
386
+ model_list_parser.add_argument("--framework", type=str, help="Filter by framework.")
387
+ model_list_parser.add_argument("--description", type=str, help="Filter by description.")
388
+ model_list_parser.add_argument("--properties", type=str, help="Comma separated string of properties key and/or properties key=value.")
389
+ model_list_parser.add_argument("--run-id", type=str, help="Filter by runId which created the model.")
390
+ model_list_parser.add_argument("--dataset-id", type=str, help="Filter by datasetId associated with the model.")
391
+ model_list_parser.add_argument("--order-by", type=str, help="How the models are ordered in the response.")
392
+ model_list_parser.add_argument("--skip-token", type=str, help="The continuation token to retrieve the next page.")
393
+ model_list_parser.add_argument("--list-view-type", type=str, default="ActiveOnly", help="View type filter (default: ActiveOnly).")
394
+
395
+ # model get command
396
+ model_get_parser = model_subparsers.add_parser("get", help="Get a model by name and version, or by asset ID.")
397
+ _add_common_args(model_get_parser, [registry_name_arg, model_subscription_arg, model_resource_group_arg, model_primary_region_arg])
398
+ model_get_parser.add_argument("--asset-id", type=str, help="Model asset ID like azureml://registries/myRegistry/models/myModel/versions/1. When using asset-id, registry details are auto-discovered.")
399
+ model_get_parser.add_argument("--name", type=str, help="The name of the model.")
400
+ model_get_parser.add_argument("--version", type=str, help="The version of the model.")
401
+ model_get_parser.add_argument("--include-deployment-settings", action="store_true", help="Whether to include deployment settings.")
402
+ model_get_parser.add_argument("--workspace-id", type=str, help="Workspace ID GUID for deployment environment association.")
403
+
404
+ # model delete command
405
+ model_delete_parser = model_subparsers.add_parser("delete", help="Delete a model by name and version, or by asset ID.")
406
+ _add_common_args(model_delete_parser, [registry_name_arg, model_subscription_arg, model_resource_group_arg, model_primary_region_arg, dry_run_arg])
407
+ model_delete_parser.add_argument("--asset-id", type=str, help="Model asset ID like azureml://registries/myRegistry/models/myModel/versions/1. When using asset-id, registry details are auto-discovered.")
408
+ model_delete_parser.add_argument("--name", type=str, help="The name of the model to delete.")
409
+ model_delete_parser.add_argument("--version", type=str, help="The version to delete. If not specified, deletes all versions.")
410
+ model_delete_parser.add_argument("--force", action="store_true", help="Skip confirmation prompt.")
411
+
296
412
  # show root command
297
413
  show_parser = subparsers.add_parser("show", help="Show syndication info.")
298
414
  _add_common_args(show_parser, [registry_name_arg, dry_run_arg])
@@ -333,7 +449,7 @@ def main() -> None:
333
449
  elif args.command == "asset":
334
450
  if args.asset_subcommand == "validate":
335
451
  # Config file is not needed for validation
336
- asset_validate(args.asset_path, args.dry_run)
452
+ asset_validate(args.asset_path, args.dry_run, args.allow_additional_properties)
337
453
  elif args.asset_subcommand == "deploy":
338
454
  # Config file is required for deployment
339
455
  asset_deploy(args.asset_path, args.config, args.dry_run)
@@ -356,6 +472,98 @@ def main() -> None:
356
472
  elif args.asset_subcommand == "template":
357
473
  # Create asset template files
358
474
  asset_template(args.folder, args.dry_run)
475
+ elif args.command == "model":
476
+ if args.model_subcommand == "list":
477
+ print(model_list(
478
+ registry_name=args.registry_name,
479
+ subscription_id=args.subscription,
480
+ resource_group_name=args.resource_group,
481
+ primary_region=args.primary_region,
482
+ name=args.name,
483
+ tags=args.tags,
484
+ version=args.version,
485
+ framework=args.framework,
486
+ description=args.description,
487
+ properties=args.properties,
488
+ run_id=args.run_id,
489
+ dataset_id=args.dataset_id,
490
+ order_by=args.order_by,
491
+ skip_token=args.skip_token,
492
+ list_view_type=args.list_view_type
493
+ ))
494
+ elif args.model_subcommand == "get":
495
+ # Validate that either asset_id is provided OR both name and version are provided
496
+ if args.asset_id:
497
+ if args.name or args.version:
498
+ print("Error: Cannot use --asset-id with --name or --version", file=sys.stderr)
499
+ sys.exit(1)
500
+ # Asset ID provided - auto-discover registry details
501
+ try:
502
+ registry_name, model_name, version = parse_asset_id(args.asset_id)
503
+ except Exception as e:
504
+ print(f"Error processing asset ID: {e}", file=sys.stderr)
505
+ sys.exit(1)
506
+ else:
507
+ # Name/version provided - validate both are present
508
+ if not args.name or not args.version:
509
+ print("Error: Both --name and --version are required when not using --asset-id", file=sys.stderr)
510
+ sys.exit(1)
511
+ registry_name = args.registry_name
512
+ model_name = args.name
513
+ version = args.version
514
+
515
+ # Validate required parameters are not None
516
+ if not registry_name or not model_name or not version:
517
+ print("Error: Registry name, model name, and version must be provided", file=sys.stderr)
518
+ sys.exit(1)
519
+
520
+ # Single call to model_get with args values directly
521
+ print(model_get(
522
+ registry_name=registry_name,
523
+ name=model_name,
524
+ version=version,
525
+ subscription_id=args.subscription,
526
+ resource_group_name=args.resource_group,
527
+ primary_region=args.primary_region,
528
+ include_deployment_settings=args.include_deployment_settings,
529
+ workspace_id=args.workspace_id
530
+ ))
531
+ elif args.model_subcommand == "delete":
532
+ # Validate that either asset_id is provided OR name is provided
533
+ if args.asset_id:
534
+ if args.name:
535
+ print("Error: Cannot use --asset-id with --name", file=sys.stderr)
536
+ sys.exit(1)
537
+ # Asset ID provided - auto-discover registry details
538
+ try:
539
+ registry_name, model_name, version = parse_asset_id(args.asset_id)
540
+ except Exception as e:
541
+ print(f"Error processing asset ID: {e}", file=sys.stderr)
542
+ sys.exit(1)
543
+ else:
544
+ if not args.name:
545
+ print("Error: --name is required when not using --asset-id", file=sys.stderr)
546
+ sys.exit(1)
547
+ registry_name = args.registry_name
548
+ model_name = args.name
549
+ version = args.version
550
+
551
+ # Validate required parameters are not None
552
+ if not registry_name or not model_name:
553
+ print("Error: Registry name and model name must be provided", file=sys.stderr)
554
+ sys.exit(1)
555
+
556
+ # Single call to model_delete with args values directly
557
+ model_delete(
558
+ registry_name=registry_name,
559
+ name=model_name,
560
+ version=version,
561
+ subscription_id=args.subscription,
562
+ resource_group_name=args.resource_group,
563
+ primary_region=args.primary_region,
564
+ dry_run=args.dry_run,
565
+ force=args.force
566
+ )
359
567
  elif args.command == "show":
360
568
  print(show_command(args.registry_name, args.as_arm_object))
361
569
  else:
@@ -0,0 +1,333 @@
1
+ # ---------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # ---------------------------------------------------------
4
+ from .base_rest_client import BaseAzureRestClient
5
+ from .registry_management_client import RegistryManagementClient
6
+ from json.decoder import JSONDecodeError
7
+ from typing import Optional, Dict, Any
8
+
9
+ DEFAULT_API_VERSION = "2025-04-01"
10
+
11
+
12
+ class RegistryModelClient(BaseAzureRestClient):
13
+ """Python client for RegistryModelController.
14
+
15
+ Provides key operations for AzureML model management: get, list and, delete models.
16
+ """
17
+
18
+ def __init__(self, registry_name: str, subscription_id: str = None, resource_group_name: str = None,
19
+ primary_region: str = None, api_key: str = None, max_retries: int = 5, backoff_factor: int = 1):
20
+ """
21
+ Initialize the RegistryModelClient.
22
+
23
+ Args:
24
+ registry_name (str): Name of the AzureML registry.
25
+ subscription_id (str, optional): Azure subscription ID. If None, auto-resolves from registry discovery.
26
+ resource_group_name (str, optional): Resource group name containing the registry. If None, auto-resolves from registry discovery.
27
+ primary_region (str, optional): Azure primary region for the registry. If None, auto-resolves from registry discovery.
28
+ api_key (str, optional): API key or bearer token. If None, uses DefaultAzureCredential.
29
+ max_retries (int): Maximum number of retries for requests.
30
+ backoff_factor (int): Backoff factor for retries.
31
+ """
32
+ # Auto-discover missing parameters using registry discovery
33
+ if subscription_id is None or resource_group_name is None or primary_region is None:
34
+ discovery = RegistryManagementClient(registry_name=registry_name).discovery()
35
+ discovered_subscription_id = discovery.get('subscriptionId')
36
+ discovered_resource_group = discovery.get('resourceGroup')
37
+ discovered_primary_region = discovery.get('primaryRegion')
38
+
39
+ # Validate that passed parameters match discovered ones
40
+ if subscription_id is not None and subscription_id != discovered_subscription_id:
41
+ raise ValueError(
42
+ f"Provided subscription_id '{subscription_id}' does not match "
43
+ f"discovered subscription_id '{discovered_subscription_id}' for registry '{registry_name}'"
44
+ )
45
+ if resource_group_name is not None and resource_group_name != discovered_resource_group:
46
+ raise ValueError(
47
+ f"Provided resource_group_name '{resource_group_name}' does not match "
48
+ f"discovered resource_group '{discovered_resource_group}' for registry '{registry_name}'"
49
+ )
50
+ if primary_region is not None and primary_region != discovered_primary_region:
51
+ raise ValueError(
52
+ f"Provided primary_region '{primary_region}' does not match "
53
+ f"discovered primary_region '{discovered_primary_region}' for registry '{registry_name}'"
54
+ )
55
+
56
+ # Use discovered values for any missing parameters
57
+ subscription_id = subscription_id or discovered_subscription_id
58
+ resource_group_name = resource_group_name or discovered_resource_group
59
+ primary_region = primary_region or discovered_primary_region
60
+
61
+ base_url = f"https://{primary_region}.api.azureml.ms"
62
+ super().__init__(base_url, api_key=api_key, max_retries=max_retries, backoff_factor=backoff_factor)
63
+
64
+ self.subscription_id = subscription_id
65
+ self.resource_group_name = resource_group_name
66
+ self.registry_name = registry_name
67
+
68
+ # Build the base path for model operations
69
+ self.base_path = (
70
+ f"/modelregistry/v1.0/subscriptions/{subscription_id}"
71
+ f"/resourceGroups/{resource_group_name}"
72
+ f"/providers/Microsoft.MachineLearningServices"
73
+ f"/registries/{registry_name}/models"
74
+ )
75
+
76
+ def list_models(self, name: str = None, tags: str = None, version: str = None,
77
+ framework: str = None, description: str = None, properties: str = None,
78
+ run_id: str = None, dataset_id: str = None, order_by: str = None,
79
+ skip_token: str = None, list_view_type: str = "ActiveOnly",
80
+ api_version: str = DEFAULT_API_VERSION, **kwargs) -> Dict[str, Any]:
81
+ """
82
+ Query the list of models in a registry.
83
+
84
+ Args:
85
+ name (str, optional): The model name.
86
+ tags (str, optional): Comma separated string of tags key or tags key=value.
87
+ version (str, optional): The model version.
88
+ framework (str, optional): The framework.
89
+ description (str, optional): The model description.
90
+ properties (str, optional): Comma separated string of properties key and/or properties key=value.
91
+ run_id (str, optional): The runId which created the model.
92
+ dataset_id (str, optional): The datasetId associated with the model.
93
+ order_by (str, optional): How the models are ordered in the response.
94
+ skip_token (str, optional): The continuation token to retrieve the next page.
95
+ list_view_type (str): View type filter (default: ActiveOnly).
96
+ api_version (str): The API version to use.
97
+ **kwargs: Additional arguments for the request.
98
+
99
+ Returns:
100
+ Dict[str, Any]: Paged response containing list of models.
101
+ """
102
+ self._refresh_api_key_if_needed()
103
+
104
+ # Build query parameters
105
+ params = {"api-version": api_version, "listViewType": list_view_type}
106
+ if name:
107
+ params["name"] = name
108
+ if tags:
109
+ params["tags"] = tags
110
+ if version:
111
+ params["version"] = version
112
+ if framework:
113
+ params["framework"] = framework
114
+ if description:
115
+ params["description"] = description
116
+ if properties:
117
+ params["properties"] = properties
118
+ if run_id:
119
+ params["runId"] = run_id
120
+ if dataset_id:
121
+ params["datasetId"] = dataset_id
122
+ if order_by:
123
+ params["orderBy"] = order_by
124
+ if skip_token:
125
+ params["$skipToken"] = skip_token
126
+
127
+ url = f"{self.base_url}{self.base_path}"
128
+ response = self.get(url, params=params, **kwargs)
129
+
130
+ try:
131
+ return response.json()
132
+ except JSONDecodeError:
133
+ raise ValueError(f"Failed to decode JSON response from {url}: {response.text.strip()}")
134
+
135
+ # Method below is not currently used in the CLI, but kept for reference
136
+ def get_model_by_asset_id(self, asset_id_or_reference: str, include_deployment_settings: bool = False,
137
+ workspace_id: str = None, api_version: str = DEFAULT_API_VERSION, **kwargs) -> Dict[str, Any]:
138
+ """
139
+ Get a model from registry using assetId or reference.
140
+
141
+ Args:
142
+ asset_id_or_reference (str): Model assetId like azureml://registries/myRegistry/models/myModel/versions/1.
143
+ include_deployment_settings (bool): Whether to include deployment settings.
144
+ workspace_id (str, optional): Workspace ID GUID for deployment environment association.
145
+ api_version (str): The API version to use.
146
+ **kwargs: Additional arguments for the request.
147
+
148
+ Returns:
149
+ Dict[str, Any]: The model data.
150
+ """
151
+ self._refresh_api_key_if_needed()
152
+
153
+ # Use the alternate endpoint
154
+ url = f"{self.base_url}/modelregistry/v1.0/registry/models"
155
+
156
+ params = {
157
+ "assetIdOrReference": asset_id_or_reference,
158
+ "includeDeploymentSettings": str(include_deployment_settings).lower(),
159
+ "api-version": api_version
160
+ }
161
+ if workspace_id:
162
+ params["workspaceId"] = workspace_id
163
+
164
+ response = self.get(url, params=params, **kwargs)
165
+
166
+ try:
167
+ return response.json()
168
+ except JSONDecodeError:
169
+ raise ValueError(f"Failed to decode JSON response from {url}: {response.text.strip()}")
170
+
171
+ def get_model_by_name_and_version(self, name: str, version: str,
172
+ api_version: str = DEFAULT_API_VERSION, **kwargs) -> Dict[str, Any]:
173
+ """
174
+ Get a specific model by name and version from the registry.
175
+
176
+ Args:
177
+ name (str): The name of the model.
178
+ version (str): The version of the model.
179
+ api_version (str): The API version to use.
180
+ **kwargs: Additional arguments for the request.
181
+
182
+ Returns:
183
+ Dict[str, Any]: The model data.
184
+
185
+ Raises:
186
+ ValueError: If name or version is not provided.
187
+ """
188
+ if not name or not version:
189
+ raise ValueError("Both name and version must be provided")
190
+
191
+ self._refresh_api_key_if_needed()
192
+
193
+ # Use the MMS ID format that the server expects: {name}:{version}
194
+ model_id = f"{name}:{version}"
195
+ url = f"{self.base_url}{self.base_path}/{model_id}"
196
+
197
+ response = self.get(url, **kwargs)
198
+
199
+ try:
200
+ return response.json()
201
+ except JSONDecodeError:
202
+ raise ValueError(f"Failed to decode JSON response from {url}: {response.text.strip()}")
203
+
204
+ def delete_model(self, name: str, version: str = None,
205
+ api_version: str = DEFAULT_API_VERSION, **kwargs) -> Optional[Dict[str, Any]]:
206
+ """
207
+ Delete a model from the registry.
208
+
209
+ Args:
210
+ name (str): The name of the model to delete.
211
+ version (str, optional): The version to delete. If None, deletes all versions.
212
+ api_version (str): The API version to use.
213
+ **kwargs: Additional arguments for the request.
214
+
215
+ Returns:
216
+ Optional[Dict[str, Any]]: The deletion response, if any.
217
+
218
+ Raises:
219
+ ValueError: If name is not provided.
220
+ """
221
+ if not name:
222
+ raise ValueError("name must be provided")
223
+
224
+ self._refresh_api_key_if_needed()
225
+
226
+ if version:
227
+ # Delete a specific version using the MMS ID format: {name}:{version}
228
+ model_id = f"{name}:{version}"
229
+ url = f"{self.base_url}{self.base_path}/{model_id}"
230
+ response = self.delete(url, **kwargs)
231
+
232
+ try:
233
+ return response.json()
234
+ except JSONDecodeError:
235
+ return response.text or None
236
+ else:
237
+ # Delete all versions: first list all versions, then delete each one
238
+ list_response = self.list_models(name=name, **kwargs)
239
+
240
+ if "value" not in list_response:
241
+ return {"message": f"No models found with name '{name}'"}
242
+
243
+ models = list_response["value"]
244
+ if not models:
245
+ return {"message": f"No models found with name '{name}'"}
246
+
247
+ deleted_versions = []
248
+ errors = []
249
+
250
+ for model in models:
251
+ try:
252
+ model_version = model.get("properties", {}).get("version") or model.get("version")
253
+ if model_version:
254
+ # Delete this specific version
255
+ model_id = f"{name}:{model_version}"
256
+ url = f"{self.base_url}{self.base_path}/{model_id}"
257
+ response = self.delete(url, **kwargs)
258
+
259
+ if response.status_code in [200, 202, 204]:
260
+ deleted_versions.append(model_version)
261
+ else:
262
+ errors.append(f"Failed to delete version {model_version}: {response.status_code}")
263
+ else:
264
+ errors.append(f"Could not determine version for model: {model}")
265
+ except Exception as e:
266
+ errors.append(f"Error deleting model version: {str(e)}")
267
+
268
+ result = {
269
+ "message": f"Deleted {len(deleted_versions)} version(s) of model '{name}'",
270
+ "deleted_versions": deleted_versions
271
+ }
272
+
273
+ if errors:
274
+ result["errors"] = errors
275
+
276
+ return result
277
+
278
+ # Method below is not currently used in the CLI, but kept for reference
279
+ def get_model_for_non_azure_accounts(self, asset_id: str, api_version: str = DEFAULT_API_VERSION, **kwargs) -> Dict[str, Any]:
280
+ """
281
+ Get a model from registry for non-Azure accounts.
282
+
283
+ Args:
284
+ asset_id (str): Model assetId like azureml://registries/myRegistry/models/myModel/versions/1.
285
+ api_version (str): The API version to use.
286
+ **kwargs: Additional arguments for the request.
287
+
288
+ Returns:
289
+ Dict[str, Any]: The model data with SAS URL.
290
+ """
291
+ self._refresh_api_key_if_needed()
292
+
293
+ url = f"{self.base_url}/modelregistry/v1.0/registry/models/nonazureaccount"
294
+ params = {
295
+ "assetId": asset_id,
296
+ "api-version": api_version
297
+ }
298
+
299
+ response = self.get(url, params=params, **kwargs)
300
+
301
+ try:
302
+ return response.json()
303
+ except JSONDecodeError:
304
+ raise ValueError(f"Failed to decode JSON response from {url}: {response.text.strip()}")
305
+
306
+ @staticmethod
307
+ def parse_asset_id(asset_id: str) -> Dict[str, str]:
308
+ """
309
+ Parse an asset ID into its components.
310
+
311
+ Args:
312
+ asset_id (str): Model assetId like azureml://registries/myRegistry/models/myModel/versions/1.
313
+
314
+ Returns:
315
+ Dict[str, str]: Dictionary containing registry_name, name, and version.
316
+
317
+ Raises:
318
+ ValueError: If the asset ID format is invalid.
319
+ """
320
+ if not asset_id.startswith("azureml://registries/"):
321
+ raise ValueError("Asset ID must start with 'azureml://registries/'")
322
+
323
+ asset_id_parts = asset_id.split("/")
324
+ if len(asset_id_parts) < 8 or asset_id_parts[4] != "models" or asset_id_parts[6] != "versions":
325
+ raise ValueError(
326
+ "Invalid asset ID format. Expected: azureml://registries/myRegistry/models/myModel/versions/1"
327
+ )
328
+
329
+ return {
330
+ "registry_name": asset_id_parts[3],
331
+ "name": asset_id_parts[5],
332
+ "version": asset_id_parts[7]
333
+ }
@@ -2,37 +2,71 @@
2
2
  # Copyright (c) Microsoft Corporation. All rights reserved.
3
3
  # ---------------------------------------------------------
4
4
 
5
- """Validate model variant schema."""
5
+ """Validate model schema."""
6
6
 
7
7
  import argparse
8
- import yaml
9
- import sys
10
8
  import jsonschema
9
+ import sys
10
+ import yaml
11
+
11
12
  from pathlib import Path
12
- from typing import List
13
+ from typing import Any, Dict, List
13
14
 
14
15
  import azureml.assets as assets
15
16
  import azureml.assets.util as util
16
17
  from azureml.assets.util import logger
17
18
 
18
19
 
20
+ def set_additional_properties_true(obj):
21
+ """Recursively set additionalProperties to True in a schema object."""
22
+ if isinstance(obj, dict):
23
+ if "additionalProperties" in obj:
24
+ obj["additionalProperties"] = True
25
+ for v in obj.values():
26
+ set_additional_properties_true(v)
27
+ elif isinstance(obj, list):
28
+ for item in obj:
29
+ set_additional_properties_true(item)
30
+
31
+
32
+ def load_schema(schema_file: Path, allow_additional_properties: bool = False) -> Dict[str, Any]:
33
+ """Load and optionally modify the schema to allow for additional properties.
34
+
35
+ Args:
36
+ schema_file (Path): Path to the schema file
37
+ allow_additional_properties (bool): Whether to allow additional properties not defined in the schema
38
+
39
+ Returns:
40
+ dict: Loaded model schema
41
+ """
42
+ # Load schema from file
43
+ with open(schema_file, 'r') as file:
44
+ schema = yaml.safe_load(file)
45
+
46
+ if allow_additional_properties:
47
+ logger.print('Allowing for additional properties, setting "additionalProperties: true" in the model schema')
48
+ set_additional_properties_true(schema)
49
+
50
+ return schema
51
+
52
+
19
53
  def validate_model_schema(input_dirs: List[Path],
20
54
  schema_file: Path,
21
- asset_config_filename: str) -> bool:
55
+ asset_config_filename: str,
56
+ allow_additional_properties: bool = False) -> bool:
22
57
  """Validate model variant schema.
23
58
 
24
59
  Args:
25
60
  input_dirs (List[Path]): Directories containing assets.
26
61
  schema_file (Path): File containing model variant schema.
27
62
  asset_config_filename (str): Asset config filename to search for.
63
+ allow_additional_properties (bool): Whether to allow additional properties not defined in schema.
28
64
 
29
65
  Returns:
30
66
  bool: True on success.
31
67
  """
32
68
  # Load model schema from file
33
- loaded_schema = {}
34
- with open(schema_file, 'r') as file:
35
- loaded_schema = yaml.safe_load(file)
69
+ loaded_schema = load_schema(schema_file, allow_additional_properties)
36
70
 
37
71
  # Create validator instance for collecting all errors
38
72
  validator = jsonschema.Draft7Validator(loaded_schema)
@@ -99,12 +133,15 @@ def validate_model_schema(input_dirs: List[Path],
99
133
 
100
134
  if __name__ == "__main__":
101
135
  # Handle command-line args
102
- parser = argparse.ArgumentParser()
136
+ parser = argparse.ArgumentParser(description="Validate model specifications against JSON schema")
103
137
  parser.add_argument("-i", "--input-dirs", required=True,
104
138
  help="Comma-separated list of directories containing assets")
105
- parser.add_argument("-m", "--schema-file", default=Path(__file__).parent / "model.schema.json", type=Path, help="Model Schema file")
139
+ parser.add_argument("-m", "--schema-file", default=Path(__file__).parent / "model.schema.json", type=Path,
140
+ help="Model Schema file")
106
141
  parser.add_argument("-a", "--asset-config-filename", default=assets.DEFAULT_ASSET_FILENAME,
107
142
  help="Asset config file name to search for")
143
+ parser.add_argument("--allow-additional-properties", action="store_true",
144
+ help="Allow additional properties not defined in the schema")
108
145
  args = parser.parse_args()
109
146
 
110
147
  # Convert comma-separated values to lists
@@ -113,7 +150,8 @@ if __name__ == "__main__":
113
150
  # Validate against model schema
114
151
  success = validate_model_schema(input_dirs=input_dirs,
115
152
  schema_file=args.schema_file,
116
- asset_config_filename=args.asset_config_filename)
153
+ asset_config_filename=args.asset_config_filename,
154
+ allow_additional_properties=args.allow_additional_properties)
117
155
 
118
156
  if not success:
119
157
  sys.exit(1)
@@ -39,11 +39,12 @@ from azureml.assets.publish_utils import create_asset # noqa: E402
39
39
  from azureml.assets.validate_assets import validate_assets # noqa: E402
40
40
 
41
41
 
42
- def validate_model(asset_path: Path) -> bool:
42
+ def validate_model(asset_path: Path, allow_additional_properties: bool = False) -> bool:
43
43
  """Validate model.
44
44
 
45
45
  Args:
46
46
  asset_path (Path): Path to the asset folder to validate
47
+ allow_additional_properties (bool): Whether to allow additional properties not defined in schema
47
48
 
48
49
  Returns:
49
50
  bool: True if validation passes, False otherwise
@@ -73,7 +74,8 @@ def validate_model(asset_path: Path) -> bool:
73
74
 
74
75
  print("⚙️ [VALIDATION #3]: Validating model schema...")
75
76
  if not validate_model_schema(input_dirs=[asset_path], schema_file=model_schema_file,
76
- asset_config_filename=assets.DEFAULT_ASSET_FILENAME):
77
+ asset_config_filename=assets.DEFAULT_ASSET_FILENAME,
78
+ allow_additional_properties=allow_additional_properties):
77
79
  print("❌ [FAILED] Validation #3: validate_model_schema\n")
78
80
  errors += 1
79
81
  else:
@@ -193,12 +195,13 @@ def create_or_update_asset(readonly_asset: AssetConfig, config: RegistryConfig):
193
195
  print(f" - Azure Portal link: https://ml.azure.com/registries/{config.registry_name}/models/{mutable_asset.name}/version/{mutable_asset.version}?tid={config.tenant_id}")
194
196
 
195
197
 
196
- def asset_validate(asset_path: Path, dry_run: bool = False) -> bool:
198
+ def asset_validate(asset_path: Path, dry_run: bool = False, allow_additional_properties: bool = False) -> bool:
197
199
  """Validate an asset at the specified path.
198
200
 
199
201
  Args:
200
202
  asset_path (Path): Path to the asset folder to validate
201
203
  dry_run (bool): If True, perform a dry run without side effects
204
+ allow_additional_properties (bool): Whether to allow additional properties not defined in schema
202
205
 
203
206
  Returns:
204
207
  bool: True if validation passes, False otherwise
@@ -231,7 +234,7 @@ def asset_validate(asset_path: Path, dry_run: bool = False) -> bool:
231
234
  return False
232
235
 
233
236
  # Perform validation
234
- return validate_model(readonly_asset.file_path)
237
+ return validate_model(readonly_asset.file_path, allow_additional_properties)
235
238
 
236
239
 
237
240
  def asset_deploy(asset_path: Path, config_path: Path, dry_run: bool = False) -> bool:
@@ -0,0 +1,169 @@
1
+ # ---------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # ---------------------------------------------------------
4
+ """Model management methods."""
5
+ import json
6
+ import sys
7
+ from typing import Tuple
8
+ from azureml.registry._rest_client.registry_model_client import RegistryModelClient
9
+
10
+
11
+ def parse_asset_id(asset_id: str) -> Tuple[str, str, str]:
12
+ """Parse an asset ID into its components.
13
+
14
+ Args:
15
+ asset_id (str): Model assetId like azureml://registries/myRegistry/models/myModel/versions/1.
16
+
17
+ Returns:
18
+ Tuple[str, str, str]: (registry_name, model_name, version)
19
+
20
+ Raises:
21
+ ValueError: If the asset ID format is invalid.
22
+ """
23
+ try:
24
+ parsed = RegistryModelClient.parse_asset_id(asset_id)
25
+ return parsed["registry_name"], parsed["name"], parsed["version"]
26
+ except ValueError as e:
27
+ raise ValueError(f"Invalid asset ID '{asset_id}': {e}")
28
+
29
+
30
+ def model_list(registry_name: str, subscription_id: str = None, resource_group_name: str = None,
31
+ primary_region: str = None, name: str = None, tags: str = None,
32
+ version: str = None, framework: str = None, description: str = None,
33
+ properties: str = None, run_id: str = None, dataset_id: str = None,
34
+ order_by: str = None, skip_token: str = None, list_view_type: str = "ActiveOnly") -> str:
35
+ """List models in the registry.
36
+
37
+ Args:
38
+ registry_name (str): Name of the Azure ML registry.
39
+ subscription_id (str, optional): Azure subscription ID. If None, auto-resolves from registry discovery.
40
+ resource_group_name (str, optional): Resource group name containing the registry. If None, auto-resolves from registry discovery.
41
+ primary_region (str, optional): Azure primary region for the registry. If None, auto-resolves from registry discovery.
42
+ name (str, optional): The object name.
43
+ tags (str, optional): Comma separated string of tags key or tags key=value.
44
+ version (str, optional): The object version.
45
+ framework (str, optional): The framework.
46
+ description (str, optional): The object description.
47
+ properties (str, optional): Comma separated string of properties key and/or properties key=value.
48
+ run_id (str, optional): The runId which created the model.
49
+ dataset_id (str, optional): The datasetId associated with the model.
50
+ order_by (str, optional): How the models are ordered in the response.
51
+ skip_token (str, optional): The continuation token to retrieve the next page.
52
+ list_view_type (str): View type filter (default: ActiveOnly).
53
+
54
+ Returns:
55
+ str: JSON response containing list of models.
56
+ """
57
+ try:
58
+ client = RegistryModelClient(
59
+ registry_name=registry_name,
60
+ subscription_id=subscription_id,
61
+ resource_group_name=resource_group_name,
62
+ primary_region=primary_region
63
+ )
64
+
65
+ result = client.list_models(
66
+ name=name, tags=tags, version=version, framework=framework,
67
+ description=description, properties=properties, run_id=run_id,
68
+ dataset_id=dataset_id, order_by=order_by, skip_token=skip_token,
69
+ list_view_type=list_view_type
70
+ )
71
+
72
+ return json.dumps(result, indent=2)
73
+
74
+ except Exception as e:
75
+ print(f"Error listing models: {e}", file=sys.stderr)
76
+ sys.exit(1)
77
+
78
+
79
+ def model_get(registry_name: str, name: str, version: str,
80
+ subscription_id: str = None, resource_group_name: str = None,
81
+ primary_region: str = None, include_deployment_settings: bool = False, workspace_id: str = None) -> str:
82
+ """Get a specific model by name and version.
83
+
84
+ Args:
85
+ registry_name (str): Name of the Azure ML registry.
86
+ name (str): The name of the model.
87
+ version (str): The version of the model.
88
+ subscription_id (str, optional): Azure subscription ID. If None, auto-resolves from registry discovery.
89
+ resource_group_name (str, optional): Resource group name containing the registry. If None, auto-resolves from registry discovery.
90
+ primary_region (str, optional): Azure primary region for the registry. If None, auto-resolves from registry discovery.
91
+ include_deployment_settings (bool): Whether to include deployment settings.
92
+ workspace_id (str, optional): Workspace ID GUID for deployment environment association.
93
+
94
+ Returns:
95
+ str: JSON response containing the model data.
96
+ """
97
+ try:
98
+ client = RegistryModelClient(
99
+ registry_name=registry_name,
100
+ subscription_id=subscription_id,
101
+ resource_group_name=resource_group_name,
102
+ primary_region=primary_region
103
+ )
104
+
105
+ result = client.get_model_by_name_and_version(
106
+ name=name,
107
+ version=version
108
+ )
109
+
110
+ return json.dumps(result, indent=2)
111
+
112
+ except Exception as e:
113
+ print(f"Error getting model {name} version {version}: {e}", file=sys.stderr)
114
+ sys.exit(1)
115
+
116
+
117
+ def model_delete(registry_name: str, name: str, version: str = None,
118
+ subscription_id: str = None, resource_group_name: str = None,
119
+ primary_region: str = None, dry_run: bool = False, force: bool = False) -> None:
120
+ """Delete a model from the registry.
121
+
122
+ Args:
123
+ registry_name (str): Name of the Azure ML registry.
124
+ name (str): The name of the model to delete.
125
+ version (str, optional): The version to delete. If None, deletes all versions.
126
+ subscription_id (str, optional): Azure subscription ID. If None, auto-resolves from registry discovery.
127
+ resource_group_name (str, optional): Resource group name containing the registry. If None, auto-resolves from registry discovery.
128
+ primary_region (str, optional): Azure primary region for the registry. If None, auto-resolves from registry discovery.
129
+ dry_run (bool): If True, do not perform any changes.
130
+ force (bool): If True, skip confirmation prompt.
131
+ """
132
+ try:
133
+ if not force:
134
+ if version:
135
+ confirm = input(f"Are you sure you want to delete model '{name}' version '{version}' from registry '{registry_name}'? [y/N]: ")
136
+ else:
137
+ confirm = input(f"Are you sure you want to delete ALL versions of model '{name}' from registry '{registry_name}'? [y/N]: ")
138
+ if confirm.lower() != "y":
139
+ print("Model deletion cancelled.")
140
+ return
141
+
142
+ if dry_run:
143
+ if version:
144
+ print(f"Dry run: Would delete model {name} version {version}")
145
+ else:
146
+ print(f"Dry run: Would delete all versions of model {name}")
147
+ return
148
+
149
+ client = RegistryModelClient(
150
+ registry_name=registry_name,
151
+ subscription_id=subscription_id,
152
+ resource_group_name=resource_group_name,
153
+ primary_region=primary_region
154
+ )
155
+
156
+ result = client.delete_model(
157
+ name=name,
158
+ version=version
159
+ )
160
+ print(result)
161
+
162
+ if version:
163
+ print(f"Successfully deleted model '{name}' version '{version}'")
164
+ else:
165
+ print(f"Successfully deleted all versions of model '{name}'")
166
+
167
+ except Exception as e:
168
+ print(f"Error deleting model {name}: {e}", file=sys.stderr)
169
+ sys.exit(1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: azureml-registry-tools
3
- Version: 0.1.0a11
3
+ Version: 0.1.0a13
4
4
  Summary: AzureML Registry tools and CLI
5
5
  Author: Microsoft Corp
6
6
  License: https://aka.ms/azureml-sdk-license
@@ -11,6 +11,7 @@ azureml/registry/_rest_client/__init__.py
11
11
  azureml/registry/_rest_client/arm_client.py
12
12
  azureml/registry/_rest_client/base_rest_client.py
13
13
  azureml/registry/_rest_client/registry_management_client.py
14
+ azureml/registry/_rest_client/registry_model_client.py
14
15
  azureml/registry/data/__init__.py
15
16
  azureml/registry/data/asset.yaml.template
16
17
  azureml/registry/data/description.md.template
@@ -26,6 +27,7 @@ azureml/registry/mgmt/asset_management.py
26
27
  azureml/registry/mgmt/create_asset_template.py
27
28
  azureml/registry/mgmt/create_manifest.py
28
29
  azureml/registry/mgmt/create_model_spec.py
30
+ azureml/registry/mgmt/model_management.py
29
31
  azureml/registry/mgmt/registry_config.py
30
32
  azureml/registry/mgmt/syndication_manifest.py
31
33
  azureml/registry/tools/__init__.py
@@ -18,7 +18,7 @@ exclude_list = ["*.tests"]
18
18
 
19
19
  setup(
20
20
  name='azureml-registry-tools',
21
- version="0.1.0a11",
21
+ version="0.1.0a13",
22
22
  description='AzureML Registry tools and CLI',
23
23
  author='Microsoft Corp',
24
24
  license="https://aka.ms/azureml-sdk-license",