Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sdk/ml/azure-ai-ml/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

### Bugs Fixed

- Fixed issue where optional string inputs with `None` or empty string values would fail with "The Value field is required" error. Optional inputs with `None` or empty string values are now correctly excluded from REST API requests, allowing the backend to use default values from component definitions.

### Other Changes


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,17 +271,30 @@ def to_rest_dataset_literal_inputs(
error_type=ValidationErrorType.INVALID_VALUE,
)
elif input_value is None:
# If the input is None, we need to pass the origin None to the REST API
input_data = LiteralJobInput(value=None)
# Skip None values for optional inputs - they should not be sent to REST API
# The backend will use the default value from the component definition if one exists
continue
else:
# otherwise, the input is a literal input
if isinstance(input_value, dict):
input_data = LiteralJobInput(value=str(input_value["value"]))
dict_value = input_value["value"]
# Skip None values for optional inputs
if dict_value is None:
continue
value_str = str(dict_value)
# Skip empty strings for optional inputs - they should not be sent to REST API
if value_str == "":
continue
input_data = LiteralJobInput(value=value_str)
# set mode attribute manually for binding job input
if "mode" in input_value:
input_data.mode = input_value["mode"]
else:
input_data = LiteralJobInput(value=str(input_value))
value_str = str(input_value)
# Skip empty strings for optional inputs - they should not be sent to REST API
if value_str == "":
continue
input_data = LiteralJobInput(value=value_str)
input_data.job_input_type = JobInputType.LITERAL
# Pack up inputs into PipelineInputs or ComponentJobInputs depending on caller
rest_inputs[input_name] = input_data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,91 @@ def test_single_docker_args_job_serialization(self) -> None:
assert job_prop.resources.locations == ["westus"]
assert hasattr(job_prop.resources, "docker_args_list") == False
assert from_rest_job.resources.docker_args == "--shm-size=1g"

def test_optional_inputs_with_none_and_empty_string(self) -> None:
"""Test that optional inputs with None or empty string values are not sent to REST API."""
# Test with None value - should be excluded from REST inputs
inputs_with_none = {
"required_input": "value1",
"optional_input": None,
}
command_job = CommandJob(
display_name="test-optional-inputs",
command='echo "hello world"',
environment="AzureML-Minimal:1",
compute="cpu-cluster",
inputs=inputs_with_none,
)
rest_obj = command_job._to_rest_object()
# None values should not be in the REST inputs
assert "required_input" in rest_obj.properties.inputs
assert "optional_input" not in rest_obj.properties.inputs

# Test with empty string value - should be excluded from REST inputs
inputs_with_empty_string = {
"required_input": "value1",
"optional_input": "",
}
command_job_empty = CommandJob(
display_name="test-optional-inputs-empty",
command='echo "hello world"',
environment="AzureML-Minimal:1",
compute="cpu-cluster",
inputs=inputs_with_empty_string,
)
rest_obj_empty = command_job_empty._to_rest_object()
# Empty string values should not be in the REST inputs
assert "required_input" in rest_obj_empty.properties.inputs
assert "optional_input" not in rest_obj_empty.properties.inputs

# Test with non-empty value - should be included in REST inputs
inputs_with_value = {
"required_input": "value1",
"optional_input": "value2",
}
command_job_value = CommandJob(
display_name="test-optional-inputs-value",
command='echo "hello world"',
environment="AzureML-Minimal:1",
compute="cpu-cluster",
inputs=inputs_with_value,
)
rest_obj_value = command_job_value._to_rest_object()
# Non-empty values should be in the REST inputs
assert "required_input" in rest_obj_value.properties.inputs
assert "optional_input" in rest_obj_value.properties.inputs
assert rest_obj_value.properties.inputs["optional_input"].value == "value2"

# Test with dict containing None value - should be excluded from REST inputs
inputs_with_dict_none = {
"required_input": "value1",
"optional_input": {"value": None},
}
command_job_dict_none = CommandJob(
display_name="test-optional-inputs-dict-none",
command='echo "hello world"',
environment="AzureML-Minimal:1",
compute="cpu-cluster",
inputs=inputs_with_dict_none,
)
rest_obj_dict_none = command_job_dict_none._to_rest_object()
# Dict with None value should not be in the REST inputs
assert "required_input" in rest_obj_dict_none.properties.inputs
assert "optional_input" not in rest_obj_dict_none.properties.inputs

# Test with dict containing empty string - should be excluded from REST inputs
inputs_with_dict_empty = {
"required_input": "value1",
"optional_input": {"value": ""},
}
command_job_dict_empty = CommandJob(
display_name="test-optional-inputs-dict-empty",
command='echo "hello world"',
environment="AzureML-Minimal:1",
compute="cpu-cluster",
inputs=inputs_with_dict_empty,
)
rest_obj_dict_empty = command_job_dict_empty._to_rest_object()
# Dict with empty string should not be in the REST inputs
assert "required_input" in rest_obj_dict_empty.properties.inputs
assert "optional_input" not in rest_obj_dict_empty.properties.inputs
Loading