Skip to content

Migrate 6 API versions to shared arm_ml_service client - #47787

Merged
saanikaguptamicrosoft merged 149 commits into
Azure:mainfrom
saanikaguptamicrosoft:saanika/migrate_tsp_final
Jul 27, 2026
Merged

Migrate 6 API versions to shared arm_ml_service client#47787
saanikaguptamicrosoft merged 149 commits into
Azure:mainfrom
saanikaguptamicrosoft:saanika/migrate_tsp_final

Conversation

@saanikaguptamicrosoft

@saanikaguptamicrosoft saanikaguptamicrosoft commented Jul 1, 2026

Copy link
Copy Markdown
Member

Notes

  • Migration tracker: https://github.com/saanikaguptamicrosoft/azure-sdk-for-python/blob/saanika/typespec-migration-analysis/sdk/ml/azure-ai-ml/docs/typespec_migration_status.md
  • Reuse the existing arm_ml_service generated client which is on stable version 2025-12-01, instead of importing from the per-version restclient package. Versions migrated-
    • 2020-09-01-dataplanepreview
    • 2021-10-01-dataplanepreview
    • 2023-04-01-preview
    • 2023-08-01-preview
    • 2024-01-01-preview
    • 2024-04-01-preview
  • Handled arm-absent models via JSON-direct hand-built wire dicts (Ray distribution, model-package tree, HDFS/Kerberos datastores, data-import sources, WorkspaceAssetReference, deployment-templates, NLP AutoML sub-models)
  • Added smoke serialization tests which can be reused for future migration work also
  • Related PR (hotfix for a Sev2): Fix datastore create TypeError: serialize msrest body for the TypeSpec client #47944 (moved to a durable long-term fix as part of this migration)
  • CI infrastructure fixes
    • macOS arm64 dependency marker excluding azureml-dataprep-rslex (no wheel on Apple Silicon) + corresponding test skips.
    • parserVersion bump (0.3.28 → 0.3.30) to clear the consistency/apistub check after the upstream apistub pin bump.

Testing

Description

Please add an informative description that covers that changes made by the pull request and link all relevant issues.

If an SDK is being regenerated based on a new API spec, a link to the pull request containing these API spec changes should be included above.

All SDK Contribution checklist:

  • The pull request does not introduce [breaking changes]
  • CHANGELOG is updated for new features, bug fixes or other significant changes.
  • I have read the contribution guidelines.

General Guidelines and Best Practices

  • Title of the pull request is clear and informative.
  • There are a small number of commits, each of which have an informative message. This means that previously merged commits do not appear in the history of the PR. For more information on cleaning up the commits in your PR, see this page.

Testing Guidelines

  • Pull request includes test coverage for the included changes.

…ression

DatastoreOperations.create_or_update was migrated to the arm_ml_service client
(which serializes the request body via json.dumps(body, cls=SdkJSONEncoder)) in
PR Azure#47554, but the datastore ENTITIES still returned v2023_04_01_preview msrest
Datastore models. SdkJSONEncoder can only serialize arm hybrid models, so every
datastore create/update raised:
    TypeError: Object of type Datastore is not JSON serializable

The bug was invisible to CI: unit tests mock the client (body never serialized),
the datastore create e2e tests are skip/live_test_only, and notebook samples reuse
the workspace default datastore instead of creating one.

Fix: migrate the datastore entities (Blob/File/ADLS Gen2/Gen1/OneLake) and the
shared datastore credentials to the arm_ml_service hybrid models so _to_rest_object()
serializes cleanly through the operation client's encoder.

Adds tests/datastore/unittests/test_datastore_serialization_regression.py: an
offline Class-A (mixed-tree) guard that serializes each datastore body via
SdkJSONEncoder exactly as the operation does.

Note: the experimental HDFS on-prem datastore (model absent in arm_ml_service)
is migrated separately via JSON-direct in the broader version migration.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a production serialization regression in azure-ai-ml datastores. In PR #47554, DatastoreOperations.create_or_update was migrated to the shared arm_ml_service generated client, whose operation serializes the request body via json.dumps(body, cls=SdkJSONEncoder, ...). However, the datastore entities still emitted v2023_04_01_preview msrest models, which SdkJSONEncoder cannot serialize, so every datastore create/update raised TypeError: Object of type Datastore is not JSON serializable. This PR migrates the datastore entities (Blob/File/ADLS Gen2/Gen1/OneLake) and the shared datastore credential conversions to the arm_ml_service hybrid models, and adds an offline regression test that serializes each datastore body exactly as the operation does.

Changes:

  • Repoint datastore entity/credential/schema REST imports from v2023_04_01_preview to arm_ml_service so _to_rest_object() produces an all-hybrid tree that serializes cleanly.
  • Add test_datastore_serialization_regression.py, an offline guard that runs each datastore body through SdkJSONEncoder.
  • Update test_datastore_schema.py assertions to expect arm_ml_service model types.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
entities/_credentials.py Move datastore credential/secret REST imports to arm_ml_service; surfaces a latent resource_uri vs resource_url mismatch for certificate creds (see comment).
entities/_datastore/datastore.py Base datastore Datastore/DatastoreType imports moved to arm_ml_service.
entities/_datastore/azure_storage.py Blob/File/ADLS Gen2 REST models moved to arm_ml_service.
entities/_datastore/adls_gen1.py ADLS Gen1 REST models moved to arm_ml_service.
entities/_datastore/one_lake.py OneLake REST models moved to arm_ml_service.
_schema/_datastore/one_lake.py DatastoreType/OneLakeArtifactType enums moved to arm_ml_service.
tests/datastore/unittests/test_datastore_serialization_regression.py New offline serialization regression guard (account-key & service-principal creds only).
tests/datastore/unittests/test_datastore_schema.py Assertions updated to expect arm_ml_service model types.

Additional notes (outside the PR diff, so not inline comments): the CHANGELOG.md "1.35.0 (unreleased) → Bugs Fixed" section is still empty; per the contribution checklist this bug fix should be documented there.

Comment thread sdk/ml/azure-ai-ml/azure/ai/ml/entities/_credentials.py Outdated
Addresses PR review: CertificateConfiguration._to_datastore_rest_object passed
resource_uri= to the arm_ml_service CertificateDatastoreCredentials model, but
that model's field is resource_url. The old msrest model silently ignored the
unknown resource_uri kwarg; the arm hybrid model raises
    TypeError: CertificateDatastoreCredentials.__init__() got an unexpected
    keyword argument 'resource_uri'
so every certificate-authenticated datastore (ADLS Gen1/Gen2) create/update
would crash - the same serialization-regression class this PR fixes.

Fix both the write (resource_url=self.resource_url) and read
(resource_url=obj.resource_url) paths, and extend the serialization regression
test to cover certificate credentials on ADLS Gen1 and Gen2 (the original test
only exercised account-key and service-principal creds).
Migrate the asset entities off the v2023_04_01_preview msrest models onto the
shared arm_ml_service hybrid client:
- Data / Model / Environment version models -> arm_ml_service.
- AutoDeleteSetting, IntellectualProperty, ModelConfiguration are absent from
  arm_ml_service (2025-12); serialize them JSON-direct (plain camelCase dict)
  and read them back via mapping access, per the reuse-existing-client approach.
- data.py: preserve the `autoDeleteSetting` wire field via mapping assignment
  (dropped from the arm model) and gate referenced_uris on the arm field set.
- environment.py: read intellectual_property defensively (arm dict or msrest),
  coerce is_anonymous default to False (arm hybrid defaults to None).
- component.py / pipeline_component.py: IntellectualProperty._to_rest_object now
  returns the wire dict directly, so drop the obsolete .serialize() call.

Validated: model, environment, dataset, model_package, component unit suites
(254 passed).
…024-01)

Migrate the entire AutoML entity surface (tabular, image, and NLP jobs plus
their settings/search-space models) off the per-version msrest restclients onto
the shared arm_ml_service hybrid client, using the proven to_hybrid_rest_model
boundary pattern for shared children (outputs/identity).

- Tabular: regression/classification/forecasting jobs + featurization/limit/
  training/forecasting settings, CustomTargetLags values->values_property,
  n_cross_validations/forecast-horizon discriminated children as wire dicts.
- Image: 4 image jobs + model/sweep/limit settings via the boundary pattern.
- NLP: text classification / multilabel / NER jobs; arm-absent NlpFixedParameters,
  NlpParameterSubspace, and NlpSweepSettings emitted JSON-direct; fixedParameters/
  searchSpace/sweepSettings/maxNodes carried as wire-keys; local NlpLearningRate
  scheduler + TrainingMode enums replace the arm-absent generated enums.

All 262 AutoML unit tests pass.
Flip the SweepJob envelope and its children to the shared arm_ml_service
hybrid model, wrapping each nested child (trial distribution/resources,
sampling_algorithm, limits, early_termination, objective, inputs, outputs,
identity, queue_settings, resources) through to_hybrid_rest_model so the
body serializes via the hybrid SdkJSONEncoder. Set is_archived=False and
attach the arm-dropped `resources` field via wire-key after construction.

- sampling_algorithm read path: arm hybrids are MutableMapping (not dict
  subclass), so read the arm-dropped `logbase` unknown wire key via
  _is_model/Mapping check instead of isinstance(obj, dict).
- Read path _load_from_rest updated for arm mapping access (resources,
  searchSpace).
- Tests: flip identity expected-models (AmlToken/ManagedIdentity/
  UserIdentity) to arm_ml_service; introspect arm-dropped logbase/resources
  via mapping access; DSL test_set_limits expects camelCase jobLimitsType
  from arm CommandJobLimits as_dict.

Sweep UTs (82) + sweep wire smoke (6) + full pipeline_job (200) + dsl +
command + job_common all green. Retires v2023_08 consumers: sweep_job,
objective, sampling_algorithm, SweepJobLimits.
…ml_service

Flip the three sweep leaf models off v2023_08_01_preview onto the shared
arm_ml_service hybrid models, now that the SweepJob envelope (and the
arm-aware DSL node serializer) consume them.

- objective.py: RestObjective -> arm.
- sampling_algorithm.py: Random/Grid/Bayesian/base + SamplingAlgorithmType
  -> arm. arm RandomSamplingAlgorithm dropped `logbase`; preserve it as an
  unknown wire key on the hybrid model in _to_rest_object (set only when
  present) and read via _is_model/Mapping in _from_rest_object.
- job_limits.py: SweepJobLimits RestSweepJobLimits -> arm (CommandJobLimits
  was already arm).

Validated: sweep UTs (82) + sweep wire smoke + command_job UTs (108 total),
full dsl + pipeline_job (490 passed, 2 skipped). Retires three more
v2023_08 consumers.
…arm_ml_service

Flip AzureOpenAiFineTuningJob and AzureOpenAiHyperparameters off
v2024_01_01_preview onto the shared arm_ml_service hybrid models, mirroring
the already-arm custom_model_finetuning_job.

- azure_openai_finetuning_job.py: RestFineTuningJob/AzureOpenAiFineTuning ->
  arm; drop the msrest _resolve_inputs/_restore_inputs overrides (the arm
  FineTuningVertical base already provides arm-correct versions); rewrite
  _to_rest_object to build the arm envelope like custom_model.
- azure_openai_hyperparameters.py: RestAzureOpenAiHyperParameters -> arm so
  the child serializes in the arm tree (fixes Class-A smoke crash where the
  msrest child couldn't serialize under the hybrid SdkJSONEncoder).
- test_finetuning_job_convesion.py: repoint Aoai* isinstance assertions to
  arm types; properties bool coerces to str "True" per arm Dict[str,str]
  wire contract (aligns with existing custom_model test).

Validated: finetuning UTs + full smoke_serialization (184 passed, incl
byte-identical aoai wire oracle). Finetuning family now fully off v2024_01
(only distillation_job.py remains on that version).
Covers the certificate datastore credential (guards the cert credential wire; resource_url intentionally omitted since its migration fix is a latent bug-fix, documented in the builder) and the credential-less (identity-based) datastore path. Byte-identical vs baseline, no mocking.
Copilot AI review requested due to automatic review settings July 24, 2026 10:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

…Ray distribution)

Adds the arm-absent, hand-built JSON-direct families that are the highest regression risk (the same class as the distillation properties-bag bug): HdfsDatastore with Kerberos password creds, DataImport with Database and FileSystem sources, and a Ray-distribution CommandJob. Byte-identical vs baseline, no mocking.
Copilot AI review requested due to automatic review settings July 24, 2026 10:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

Adds a Model with default/allowed deployment templates (arm-absent template fields with special wire-keying and the msrest stage-drop) and a WorkspaceAssetReference (arm-absent registry copy, hand-built JSON-direct wire). Byte-identical vs baseline, no mocking.
Copilot AI review requested due to automatic review settings July 24, 2026 10:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

@github-actions

This comment has been minimized.

…ry-create None

Two notebook-caught regressions in the Environment migration:

1. inference_config passed as a raw snake_case dict (direct Environment(...) construction) was serialized verbatim by the arm hybrid encoder, so the server rejected it ('must specify all three of: liveness, readiness and scoring routes'). msrest's typed field mapped it snake->camel. Now convert the dict to an arm InferenceContainerProperties (livenessRoute/readinessRoute/scoringRoute); already-model inputs pass through unchanged.

2. Registry environment create wrapped the LRO result in EnvironmentVersion._deserialize eagerly; the registry create can return an empty (None) body, causing 'NoneType has no attribute items'. Only deserialize non-None; otherwise fall through to the existing _get re-fetch guard.
Copilot AI review requested due to automatic review settings July 24, 2026 14:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

…e None guard

Regression coverage for the two notebook-caught Environment bugs:

- smoke: Environment(inference_config={raw dict}) and the arm-model path must both emit camelCase livenessRoute/readinessRoute/scoringRoute (explicit assertion; baseline cannot serialize the raw-dict input).

- unit: registry create returning an empty (None) LRO body must re-fetch via _get instead of crashing in _deserialize(None).
Copilot AI review requested due to automatic review settings July 24, 2026 14:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

…vironment fix)

Static audit of _deserialize(<registry create helper>) sites found the same latent bug as the environment registry create: code and data registry create wrapped begin_create_or_update_registry_versioned_asset in _deserialize eagerly, so an empty (None) LRO body would raise 'NoneType' object has no attribute 'items' before the existing 'if not result: re-fetch' guard could run. Only deserialize non-None results.
Copilot AI review requested due to automatic review settings July 24, 2026 14:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor
[Pilot] PR Pipeline Failure Analysis

A CI pipeline failed on this pull request. Here is an automated analysis of what went wrong and how to get the build green.

What failed

The Pylint check for azure-ai-ml failed with exit code 16 (build log). The root cause is 3 docstring violations in azure/ai/ml/entities/_assets/environment.py at line 41 (method _to_rest_inference_config):

  • C4739inference_config parameter is missing from the docstring
  • C4741 – Return description is missing from the docstring
  • C4742 – Return type is missing from the docstring

Recommended next steps

  • In sdk/ml/azure-ai-ml/azure/ai/ml/entities/_assets/environment.py, update the docstring of _to_rest_inference_config (line 41) to add:
    • an :param inference_config: entry with a description
    • a :return: entry with a description
    • a :rtype: entry with the return type
  • Run azpysdk pylint . locally from sdk/ml/azure-ai-ml/ to confirm the violations are resolved before pushing
  • See the CI troubleshooting guide: https://aka.ms/ci-fix
  • Push new commits to address the failures; this comment updates automatically on the next failing run.
Raw pipeline analysis (azsdk ci analyze)
Failed Tasks
### Errors:
[azure-ai-ml :: pylint] ************* Module azure.ai.ml.entities._assets.environment
[azure-ai-ml :: pylint] azure/ai/ml/entities/_assets/environment.py:41: [C4739(docstring-missing-param), _to_rest_inference_config] Params missing in docstring: "inference_config". See details: (azure.github.io/redacted)
[azure-ai-ml :: pylint] azure/ai/ml/entities/_assets/environment.py:41: [C4741(docstring-missing-return), _to_rest_inference_config] A return doc is missing in the docstring. See details: (azure.github.io/redacted)
[azure-ai-ml :: pylint] azure/ai/ml/entities/_assets/environment.py:41: [C4742(docstring-missing-rtype), _to_rest_inference_config] A return type is missing in the docstring. See details: (azure.github.io/redacted)

[azure-ai-ml :: pylint] 2026-07-24 16:43:36,162 [ERROR] azure-sdk-tools: azure-ai-ml main package exited with linting error 16.
[azure-ai-ml :: pylint] pylint check completed with exit code 16

=== SUMMARY ===
/mnt/vss/_work/1/s/sdk/ml/azure-ai-ml  pylint  FAIL(16)  104.14s
Total checks: 1 | Failed: 1 | Worst exit code: 16

Pipeline: https://dev.azure.com/azure-sdk/public/_build/results?buildId=6613718

Copilot detected the failing pipeline and generated the analysis above. To have it attempt a fix automatically, reply with @copilot please fix the failing pipeline on this PR.

Generated by Pipeline Analysis - Next Steps · 33 AIC · ⌖ 6.19 AIC · ⊞ 6.7K ·

…nt docstring

Fixes several AttributeError regressions where migrated read paths call msrest-only APIs on arm_ml_service hybrid models (which are MutableMappings without those attributes):

- kubernetes_compute._load_from_rest: prop.additional_properties.get(createdOn) -> mapping-safe read (like aml_compute already does).

- pipeline_component_batch_deployment._from_rest_object: deployment.properties.additional_properties[deploymentConfiguration] -> mapping-safe read.

- _model_operations package(): additional_properties[targetEnvironmentId] else-branch made mapping-safe.

- Registry create (code/data/environment): the create LRO body can be incomplete (missing the asset id, which _from_rest_object parses via AMLVersionedArmId -> 'no attribute asset_name'); re-fetch via get() when the id is missing.

- environment: add pylint docstring param/return/rtype to _to_rest_inference_config (fixes Build Analyze C4739/C4741/C4742).
Copilot AI review requested due to automatic review settings July 24, 2026 17:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

…O response)

The registry create re-fetch guard (adf137a) re-fetches via get() when the create result lacks an id. test_show_progress_disabled_env mocked begin_create_or_update_registry_versioned_asset to return {properties:{}} (no id), which triggered a _get on the None mock registry client. The real registry create LRO returns the resource with its id; make the mock realistic so the guard is a no-op and the test exercises show_progress passthrough as intended.
Copilot AI review requested due to automatic review settings July 24, 2026 17:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

@saanikaguptamicrosoft
saanikaguptamicrosoft merged commit 48b0aa0 into Azure:main Jul 27, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants