Sometimes the list items each need two fields — a date and a summary. You could try list[Event] as result_type, but the cleaner move is to wrap the list inside a single Pydantic class. Why do you think that is?
Because a list[Model] gives back a bare list I still have to iterate and serialize? Whereas one wrapper class gives me one .model_dump() call?
Exactly that. One EventList(events: list[Event]) class returns one EventList instance, and .model_dump() flattens everything at once. The model definitions:
from pydantic import BaseModel
class Event(BaseModel):
date: str
summary: str
class EventList(BaseModel):
events: list[Event]So result.output is an EventList instance, and calling .model_dump() on it gives {"events": [{...}, {...}]}?
One dump, nested list inside. The full extractor:
def get_upcoming_events(text: str) -> dict:
agent = Agent(model, result_type=EventList)
result = agent.run_sync(text)
data = result.output.model_dump()
print(f"Agent events: {data}")
return dataReturning a dict with "events" as a key — why not just the list?
Two reasons. First, .model_dump() from a wrapper class flattens nested Pydantic items cleanly in one call — no per-item comprehension. Second, a dict with a named key is easier to evolve: add more fields to EventList later (e.g. summary_line) without changing the return shape.
So wrapping is the shape for any extraction where the items are themselves typed — I get one dict out, nested types flattened, ready to ship?
Wrap, validate, dump. This is the cleanest pattern for "list of typed things from one agent call," and it extends to triply-nested structures the same way.
TL;DR: wrap list[Event] inside an EventList class so one .model_dump() flattens everything.
class Event(BaseModel) — per-item shapeclass EventList(BaseModel) — holds events: list[Event].model_dump() — one call flattens nested types to plain dicts| Pattern | Serialization | Extensibility |
|---|---|---|
list[Event] | per-item loop | add fields = new model |
EventList | one dump | add fields to wrapper |
Wrappers scale cleanly as the shape grows — a one-class change carries the whole pipeline.
Sometimes the list items each need two fields — a date and a summary. You could try list[Event] as result_type, but the cleaner move is to wrap the list inside a single Pydantic class. Why do you think that is?
Because a list[Model] gives back a bare list I still have to iterate and serialize? Whereas one wrapper class gives me one .model_dump() call?
Exactly that. One EventList(events: list[Event]) class returns one EventList instance, and .model_dump() flattens everything at once. The model definitions:
from pydantic import BaseModel
class Event(BaseModel):
date: str
summary: str
class EventList(BaseModel):
events: list[Event]So result.output is an EventList instance, and calling .model_dump() on it gives {"events": [{...}, {...}]}?
One dump, nested list inside. The full extractor:
def get_upcoming_events(text: str) -> dict:
agent = Agent(model, result_type=EventList)
result = agent.run_sync(text)
data = result.output.model_dump()
print(f"Agent events: {data}")
return dataReturning a dict with "events" as a key — why not just the list?
Two reasons. First, .model_dump() from a wrapper class flattens nested Pydantic items cleanly in one call — no per-item comprehension. Second, a dict with a named key is easier to evolve: add more fields to EventList later (e.g. summary_line) without changing the return shape.
So wrapping is the shape for any extraction where the items are themselves typed — I get one dict out, nested types flattened, ready to ship?
Wrap, validate, dump. This is the cleanest pattern for "list of typed things from one agent call," and it extends to triply-nested structures the same way.
TL;DR: wrap list[Event] inside an EventList class so one .model_dump() flattens everything.
class Event(BaseModel) — per-item shapeclass EventList(BaseModel) — holds events: list[Event].model_dump() — one call flattens nested types to plain dicts| Pattern | Serialization | Extensibility |
|---|---|---|
list[Event] | per-item loop | add fields = new model |
EventList | one dump | add fields to wrapper |
Wrappers scale cleanly as the shape grows — a one-class change carries the whole pipeline.
Create a free account to get started. Paid plans unlock all tracks.