refactor: add Field descriptions, explicit defaults, and ConfigDict to A2UI catalog models

This commit is contained in:
Greyson Lalonde
2026-03-14 15:25:20 -04:00
parent e63c6310ed
commit afb6cbbb6e

View File

@@ -8,134 +8,164 @@ from __future__ import annotations
from typing import Literal
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field
class StringBinding(BaseModel):
"""A string value: literal or data-model path."""
literal_string: str | None = Field(None, alias="literalString")
path: str | None = None
literal_string: str | None = Field(
default=None, alias="literalString", description="Literal string value."
)
path: str | None = Field(default=None, description="Data-model path reference.")
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class NumberBinding(BaseModel):
"""A numeric value: literal or data-model path."""
literal_number: float | None = Field(None, alias="literalNumber")
path: str | None = None
literal_number: float | None = Field(
default=None, alias="literalNumber", description="Literal numeric value."
)
path: str | None = Field(default=None, description="Data-model path reference.")
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class BooleanBinding(BaseModel):
"""A boolean value: literal or data-model path."""
literal_boolean: bool | None = Field(None, alias="literalBoolean")
path: str | None = None
literal_boolean: bool | None = Field(
default=None, alias="literalBoolean", description="Literal boolean value."
)
path: str | None = Field(default=None, description="Data-model path reference.")
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class ArrayBinding(BaseModel):
"""An array value: literal or data-model path."""
literal_array: list[str] | None = Field(None, alias="literalArray")
path: str | None = None
literal_array: list[str] | None = Field(
default=None, alias="literalArray", description="Literal array of strings."
)
path: str | None = Field(default=None, description="Data-model path reference.")
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class ChildrenDef(BaseModel):
"""Children definition for layout components."""
explicit_list: list[str] | None = Field(None, alias="explicitList")
template: ChildTemplate | None = None
explicit_list: list[str] | None = Field(
default=None,
alias="explicitList",
description="Explicit list of child component IDs.",
)
template: ChildTemplate | None = Field(
default=None, description="Template for generating dynamic children."
)
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class ChildTemplate(BaseModel):
"""Template for generating dynamic children from a data model list."""
component_id: str = Field(alias="componentId")
data_binding: str = Field(alias="dataBinding")
component_id: str = Field(
alias="componentId", description="ID of the component to repeat."
)
data_binding: str = Field(
alias="dataBinding", description="Data-model path to bind the template to."
)
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class ActionContextEntry(BaseModel):
"""A key-value pair in an action context payload."""
key: str
value: ActionBoundValue
key: str = Field(description="Context entry key.")
value: ActionBoundValue = Field(description="Context entry value.")
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class ActionBoundValue(BaseModel):
"""A value in an action context: literal or data-model path."""
path: str | None = None
literal_string: str | None = Field(None, alias="literalString")
literal_number: float | None = Field(None, alias="literalNumber")
literal_boolean: bool | None = Field(None, alias="literalBoolean")
path: str | None = Field(default=None, description="Data-model path reference.")
literal_string: str | None = Field(
default=None, alias="literalString", description="Literal string value."
)
literal_number: float | None = Field(
default=None, alias="literalNumber", description="Literal numeric value."
)
literal_boolean: bool | None = Field(
default=None, alias="literalBoolean", description="Literal boolean value."
)
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class Action(BaseModel):
"""Client-side action dispatched by interactive components."""
name: str
context: list[ActionContextEntry] | None = None
name: str = Field(description="Action name dispatched on interaction.")
context: list[ActionContextEntry] | None = Field(
default=None, description="Key-value pairs sent with the action."
)
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class TabItem(BaseModel):
"""A single tab definition."""
title: StringBinding
child: str
title: StringBinding = Field(description="Tab title text.")
child: str = Field(description="Component ID rendered as the tab content.")
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class MultipleChoiceOption(BaseModel):
"""A single option in a MultipleChoice component."""
label: StringBinding
value: str
label: StringBinding = Field(description="Display label for the option.")
value: str = Field(description="Value submitted when the option is selected.")
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class Text(BaseModel):
"""Displays text content."""
text: StringBinding
text: StringBinding = Field(description="Text content to display.")
usage_hint: Literal["h1", "h2", "h3", "h4", "h5", "caption", "body"] | None = Field(
None, alias="usageHint"
default=None, alias="usageHint", description="Semantic hint for text styling."
)
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class Image(BaseModel):
"""Displays an image."""
url: StringBinding
fit: Literal["contain", "cover", "fill", "none", "scale-down"] | None = None
url: StringBinding = Field(description="Image source URL.")
fit: Literal["contain", "cover", "fill", "none", "scale-down"] | None = Field(
default=None, description="Object-fit behavior for the image."
)
usage_hint: (
Literal[
"icon", "avatar", "smallFeature", "mediumFeature", "largeFeature", "header"
]
| None
) = Field(None, alias="usageHint")
) = Field(
default=None, alias="usageHint", description="Semantic hint for image sizing."
)
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
IconName = Literal[
@@ -193,168 +223,224 @@ IconName = Literal[
class IconBinding(BaseModel):
"""Icon name: literal enum or data-model path."""
literal_string: IconName | None = Field(None, alias="literalString")
path: str | None = None
literal_string: IconName | None = Field(
default=None, alias="literalString", description="Literal icon name."
)
path: str | None = Field(default=None, description="Data-model path reference.")
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class Icon(BaseModel):
"""Displays a named icon."""
name: IconBinding
name: IconBinding = Field(description="Icon name binding.")
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class Video(BaseModel):
"""Displays a video player."""
url: StringBinding
url: StringBinding = Field(description="Video source URL.")
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class AudioPlayer(BaseModel):
"""Displays an audio player."""
url: StringBinding
description: StringBinding | None = None
url: StringBinding = Field(description="Audio source URL.")
description: StringBinding | None = Field(
default=None, description="Accessible description of the audio content."
)
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class Row(BaseModel):
"""Horizontal layout container."""
children: ChildrenDef
children: ChildrenDef = Field(description="Child components in this row.")
distribution: (
Literal["center", "end", "spaceAround", "spaceBetween", "spaceEvenly", "start"]
| None
) = None
alignment: Literal["start", "center", "end", "stretch"] | None = None
) = Field(
default=None, description="How children are distributed along the main axis."
)
alignment: Literal["start", "center", "end", "stretch"] | None = Field(
default=None, description="How children are aligned on the cross axis."
)
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class Column(BaseModel):
"""Vertical layout container."""
children: ChildrenDef
children: ChildrenDef = Field(description="Child components in this column.")
distribution: (
Literal["start", "center", "end", "spaceBetween", "spaceAround", "spaceEvenly"]
| None
) = None
alignment: Literal["center", "end", "start", "stretch"] | None = None
) = Field(
default=None, description="How children are distributed along the main axis."
)
alignment: Literal["center", "end", "start", "stretch"] | None = Field(
default=None, description="How children are aligned on the cross axis."
)
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class List(BaseModel):
"""Scrollable list container."""
children: ChildrenDef
direction: Literal["vertical", "horizontal"] | None = None
alignment: Literal["start", "center", "end", "stretch"] | None = None
children: ChildrenDef = Field(description="Child components in this list.")
direction: Literal["vertical", "horizontal"] | None = Field(
default=None, description="Scroll direction of the list."
)
alignment: Literal["start", "center", "end", "stretch"] | None = Field(
default=None, description="How children are aligned on the cross axis."
)
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class Card(BaseModel):
"""Card container wrapping a single child."""
child: str
child: str = Field(description="Component ID of the card content.")
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class Tabs(BaseModel):
"""Tabbed navigation container."""
tab_items: list[TabItem] = Field(alias="tabItems")
tab_items: list[TabItem] = Field(
alias="tabItems", description="List of tab definitions."
)
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class Divider(BaseModel):
"""A visual divider line."""
axis: Literal["horizontal", "vertical"] | None = None
axis: Literal["horizontal", "vertical"] | None = Field(
default=None, description="Orientation of the divider."
)
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class Modal(BaseModel):
"""A modal dialog with an entry point trigger and content."""
entry_point_child: str = Field(alias="entryPointChild")
content_child: str = Field(alias="contentChild")
entry_point_child: str = Field(
alias="entryPointChild", description="Component ID that triggers the modal."
)
content_child: str = Field(
alias="contentChild", description="Component ID rendered inside the modal."
)
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class Button(BaseModel):
"""An interactive button with an action."""
child: str
primary: bool | None = None
action: Action
child: str = Field(description="Component ID of the button label.")
primary: bool | None = Field(
default=None, description="Whether the button uses primary styling."
)
action: Action = Field(description="Action dispatched when the button is clicked.")
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class CheckBox(BaseModel):
"""A checkbox input."""
label: StringBinding
value: BooleanBinding
label: StringBinding = Field(description="Label text for the checkbox.")
value: BooleanBinding = Field(
description="Boolean value binding for the checkbox state."
)
model_config = {"extra": "forbid"}
model_config = ConfigDict(extra="forbid")
class TextField(BaseModel):
"""A text input field."""
label: StringBinding
text: StringBinding | None = None
label: StringBinding = Field(description="Label text for the input.")
text: StringBinding | None = Field(
default=None, description="Current text value binding."
)
text_field_type: (
Literal["date", "longText", "number", "shortText", "obscured"] | None
) = Field(None, alias="textFieldType")
validation_regexp: str | None = Field(None, alias="validationRegexp")
) = Field(default=None, alias="textFieldType", description="Input type variant.")
validation_regexp: str | None = Field(
default=None,
alias="validationRegexp",
description="Regex pattern for client-side validation.",
)
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class DateTimeInput(BaseModel):
"""A date and/or time picker."""
value: StringBinding
enable_date: bool | None = Field(None, alias="enableDate")
enable_time: bool | None = Field(None, alias="enableTime")
value: StringBinding = Field(description="ISO date/time string value binding.")
enable_date: bool | None = Field(
default=None,
alias="enableDate",
description="Whether the date picker is enabled.",
)
enable_time: bool | None = Field(
default=None,
alias="enableTime",
description="Whether the time picker is enabled.",
)
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class MultipleChoice(BaseModel):
"""A multiple-choice selection component."""
selections: ArrayBinding
options: list[MultipleChoiceOption]
max_allowed_selections: int | None = Field(None, alias="maxAllowedSelections")
variant: Literal["checkbox", "chips"] | None = None
filterable: bool | None = None
selections: ArrayBinding = Field(description="Array binding for selected values.")
options: list[MultipleChoiceOption] = Field(description="Available choices.")
max_allowed_selections: int | None = Field(
default=None,
alias="maxAllowedSelections",
description="Maximum number of selections allowed.",
)
variant: Literal["checkbox", "chips"] | None = Field(
default=None, description="Visual variant for the selection UI."
)
filterable: bool | None = Field(
default=None, description="Whether options can be filtered by typing."
)
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
class Slider(BaseModel):
"""A numeric slider input."""
value: NumberBinding
min_value: float | None = Field(None, alias="minValue")
max_value: float | None = Field(None, alias="maxValue")
value: NumberBinding = Field(
description="Numeric value binding for the slider position."
)
min_value: float | None = Field(
default=None, alias="minValue", description="Minimum slider value."
)
max_value: float | None = Field(
default=None, alias="maxValue", description="Maximum slider value."
)
model_config = {"populate_by_name": True, "extra": "forbid"}
model_config = ConfigDict(populate_by_name=True, extra="forbid")
STANDARD_CATALOG_COMPONENTS: frozenset[str] = frozenset(