[BUG]: Python v0.9/v0.9.1 catalogs reject the specified `FunctionCall.returnType`
Describe the Bug
The Python SDK validates v0.9 and v0.9.1 function calls against the v1.0 shape.
FunctionCall is defined once, in
python/a2ui_core/src/a2ui/core/schema/common_types.py,
and every version package imports it. Its fields are call, args and
catalogId, and it derives from StrictBaseModel, which sets
extra="forbid". That field set is the v1.0 one:
v0.9 / v0.9.1 common_types.json |
v1.0 common_types.json |
Python model | |
|---|---|---|---|
call |
yes | yes (via FunctionCommon) |
yes |
returnType |
yes, default "boolean" |
removed | no |
catalogId |
no | yes (via FunctionCommon) |
yes |
Two consequences for v0.9 and v0.9.1:
- A payload carrying
returnTypeis rejected, although the specification defines the field.extra="forbid"turns the unknown key into an error. - A payload carrying
catalogIdis accepted, although the version does not define it.
returnType is load-bearing in v0.9, not decorative. DynamicString,
DynamicNumber and DynamicBoolean each constrain their function branch with
allOf: [FunctionCall, {properties: {returnType: {const: "string"}}}], so the
field is how the document states the expected return type of a call in a typed
slot.
Steps to Reproduce
from a2ui.core.schema.common_types import FunctionCall
FunctionCall.model_validate({"call": "f"}) # ok
FunctionCall.model_validate({"call": "f", "catalogId": "c"}) # ok, but v1.0-only
FunctionCall.model_validate({"call": "f", "returnType": "string"}) # raisesThe third call fails with:
1 validation error for FunctionCall
returnType
Extra inputs are not permitted [type=extra_forbidden, ...]The same gap appears at the schema layer. Validating a v0.9 DynamicString
slot against the v0.9 BasicCatalog schema, versus against
specification/v0_9/json/common_types.json:
payload BasicCatalog specification
{"call": "f"} accepts accepts
{"call": "f", "returnType": "string"} rejects accepts
{"call": "f", "returnType": "number"} rejects rejectsExpected Behavior
A catalog built for v0.9 or v0.9.1 accepts returnType, and enforces it where
the version's Dynamic* definitions require a particular value. Whether
catalogId should be rejected on those versions or accepted as an extension is
a call for the maintainers; today it is accepted silently.
Additional Context
- Scope: this is one shared model, not the version-specific component models.
The v0.9 models supply correct version-specific definitions elsewhere. For
example the v0.9
AccessibilityAttributescorrectly carries onlylabelanddescription, without thehiddenandlivethat v1.0 added. - Related but separate: the hardcoded fallback definitions in
_get_dynamic_types_defs()(catalog.py) are also v1.0-shaped. Their reach is small, since definitions from the version's own models and from a caller-suppliedcommon_types.jsonboth take precedence. For the v0.9BasicCatalogonlyComponentId,DynamicBoolean,DynamicNumber,DynamicStringandDynamicValuecome from the fallback. The practical effect there is the lostreturnTypeconstraint on the three scalar wrappers, which is the same root cause as above. - Detection:
python/a2ui_core/tests/test_specification_drift.pycompares the properties each version'sBasicCatalogaccepts against that version's publishedcommon_types.json. The divergence is recorded there inKNOWN_PROPERTY_GAPSunderv0_9andv0_9_1. Fixing this issue means deleting those two entries; the test fails if a recorded gap closes. - Not verified: whether the TypeScript
web_corev0.9 path has the same gap.
Environment Details
- SDK/Package Name & Version:
a2ui-core(Python),main - Protocol Version: v0.9 and v0.9.1
Source: a2ui-project/a2ui