Skip to content
Merged
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
66 changes: 66 additions & 0 deletions tests/test_ai_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,72 @@ def test_saved_model_schema_shorthand_compiles_closed_required_nested_objects():
}


def test_blank_nested_schema_keywords_use_object_defaults():
saved = _saved_model(
[
"Voltage",
"Voltage and unit",
"object",
"",
"",
"",
"",
(
'{"value":{"type":"number","nullable"},'
'"uom":{"type":"string","nullable"}}'
),
],
[
"Cutting Depth",
"Cutting depth by material",
"array",
"",
"",
"",
"",
"",
(
'{"type":"object","properties":{'
'"value":{"type":"number","nullable"},'
'"material":{"type":"string","nullable"}},'
'"required","additionalProperties","nullable"}'
),
],
columns=[
"Find",
"Description",
"Type",
"Default",
"Examples",
"Enum",
"Notes",
"Properties",
"Items",
],
)

compiled = ai_definition.compile_definition(
None,
model="gpt-5.4-mini",
saved_model_content=saved,
)

voltage = compiled.output["Voltage"]
assert voltage["type"] == ["object", "null"]
assert voltage["properties"] == {
"value": {"type": "number"},
"uom": {"type": "string"},
}

cutting_depth_items = compiled.output["Cutting_Depth"]["items"]
assert cutting_depth_items["required"] == ["value", "material"]
assert cutting_depth_items["additionalProperties"] is False
assert cutting_depth_items["properties"] == {
"value": {"type": "number"},
"material": {"type": "string"},
}


def test_saved_model_human_values_are_shape_aware_and_json_compatible():
saved = _saved_model(
["Power Source", "", "string", "", "Corded|Battery", "Corded|Battery", "", ""],
Expand Down
8 changes: 8 additions & 0 deletions wrangles/ai_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,14 @@ def normalize_schema(

node = {}
for key, item in _copy.deepcopy(value).items():
# YAML-like entries such as `{nullable}` are blank Excel values.
# Omit only keywords whose documented behavior has a safe default.
if (
key in {"additionalProperties", "nullable", "required"}
and isinstance(item, str)
and not item.strip()
):
continue
parser = {
"additionalProperties": self._parse_additional_properties_value,
"anyOf": self._parse_list_value,
Expand Down