Summary
The Python generator appears to lose a flattened tagged-union field when generating offers_search::Offer.
In the ReflectAPI schema, offers_search::Offer has:
id
payload: offers_search::OfferKind
payload is marked "flattened": true
The generated Python client emits OffersSearchOffer with only id, while generating OffersSearchOfferKind* separately but never composing them back into OffersSearchOffer.
This causes real API payload fields like type, business, and nested payload to be silently dropped because the generated Python models use extra="ignore".
Minimal ReflectAPI snippet
From reflectapi.json:
{
"name": "offer_recommendation::RepairerRecommendations",
"fields": {
"named": [
{
"name": "offers",
"type": {
"name": "std::vec::Vec",
"arguments": [
{
"name": "offers_search::Offer"
}
]
},
"required": true
}
]
}
}
{
"name": "offers_search::Offer",
"fields": {
"named": [
{
"name": "id",
"type": {
"name": "std::string::String"
},
"required": true
},
{
"name": "payload",
"type": {
"name": "offers_search::OfferKind"
},
"required": true,
"flattened": true
}
]
}
}
Generated Python output
Generated Python currently produces:
class OffersSearchOffer(BaseModel):
model_config = ConfigDict(extra="ignore", populate_by_name=True)
id: str
class OffersSearchOfferKindSingle(BaseModel):
model_config = ConfigDict(extra="ignore", populate_by_name=True)
type: Literal["single"] = "single"
business: SupplySearchBusiness
payload: OffersSearchSellableKind
class OffersSearchOfferKindGroup(BaseModel):
model_config = ConfigDict(extra="ignore", populate_by_name=True)
type: Literal["group"] = "group"
status: SupplySearchGroupOfferStatus
offers: list[OffersSearchSellableOffer]
class OffersSearchOfferKindRequest(BaseModel):
model_config = ConfigDict(extra="ignore", populate_by_name=True)
type: Literal["request"] = "request"
offer: OffersSearchSellableOffer
note: str | None = None
subject: OfferRequestSubjectV2 | None = None
is_override: bool | None = None
class OffersSearchOfferKind(RootModel):
root: Annotated[
Union[
OffersSearchOfferKindSingle,
OffersSearchOfferKindGroup,
OffersSearchOfferKindRequest,
],
Field(discriminator="type"),
]
The problem is that OffersSearchOffer never includes the flattened OfferKind.
Expected Python shape
Roughly something like:
class OffersSearchOffer(BaseModel):
model_config = ConfigDict(extra="ignore", populate_by_name=True)
id: str
payload: OffersSearchOfferKind = Field(flattened=True)
Or any equivalent generated representation that preserves the flattened tagged union when validating and dumping JSON.
Evidence from other generators
The same schema is represented correctly in other generated clients.
TypeScript:
export type Offer = {
id: string;
} & NullToEmptyObject<offers_search.OfferKind>;
Rust:
pub struct Offer {
pub id: String,
#[serde(flatten)]
pub payload: super::offers_search::OfferKind,
}
Impact
A real response object like:
{
"id": "99742bbe-b189-4731-8553-7ce63e60860e",
"type": "single",
"business": {
"name": "Brown Auto Parts"
},
"payload": {
"kind": "product"
}
}
validates into the generated Python OffersSearchOffer as effectively:
{
"id": "99742bbe-b189-4731-8553-7ce63e60860e"
}
because the flattened fields are dropped.
Summary
The Python generator appears to lose a flattened tagged-union field when generating
offers_search::Offer.In the ReflectAPI schema,
offers_search::Offerhas:idpayload: offers_search::OfferKindpayloadis marked"flattened": trueThe generated Python client emits
OffersSearchOfferwith onlyid, while generatingOffersSearchOfferKind*separately but never composing them back intoOffersSearchOffer.This causes real API payload fields like
type,business, and nestedpayloadto be silently dropped because the generated Python models useextra="ignore".Minimal ReflectAPI snippet
From
reflectapi.json:{ "name": "offer_recommendation::RepairerRecommendations", "fields": { "named": [ { "name": "offers", "type": { "name": "std::vec::Vec", "arguments": [ { "name": "offers_search::Offer" } ] }, "required": true } ] } }{ "name": "offers_search::Offer", "fields": { "named": [ { "name": "id", "type": { "name": "std::string::String" }, "required": true }, { "name": "payload", "type": { "name": "offers_search::OfferKind" }, "required": true, "flattened": true } ] } }Generated Python output
Generated Python currently produces:
The problem is that
OffersSearchOffernever includes the flattenedOfferKind.Expected Python shape
Roughly something like:
Or any equivalent generated representation that preserves the flattened tagged union when validating and dumping JSON.
Evidence from other generators
The same schema is represented correctly in other generated clients.
TypeScript:
Rust:
Impact
A real response object like:
{ "id": "99742bbe-b189-4731-8553-7ce63e60860e", "type": "single", "business": { "name": "Brown Auto Parts" }, "payload": { "kind": "product" } }validates into the generated Python
OffersSearchOfferas effectively:{ "id": "99742bbe-b189-4731-8553-7ce63e60860e" }because the flattened fields are dropped.