Pydantic and dataclasses
Define application inputs and outputs around an async function that calls rboto. You choose the fields and validation rules. Client methods keep their generated signatures, and responses keep their native, read-only attributes.
Pydantic inputs and outputs
Install Pydantic 2 alongside the clients you use:
rboto does not require Pydantic. The following example assumes that you have installed the S3 client as described in Getting started.
This tool accepts an object key and returns the object's size and content type:
from pydantic import BaseModel, ConfigDict, Field
from rboto import S3Client
class DocumentInput(BaseModel):
model_config = ConfigDict(strict=True, extra="forbid")
key: str = Field(min_length=1, description="Object key within the document bucket")
class DocumentOutput(BaseModel):
model_config = ConfigDict(from_attributes=True, strict=True)
content_length: int = Field(ge=0)
content_type: str | None = None
async def inspect_document(
client: S3Client,
data: DocumentInput,
*,
bucket: str,
) -> DocumentOutput:
response = await client.head_object(bucket=bucket, key=data.key)
return DocumentOutput.model_validate(response)
async def run_tool(client: S3Client, arguments_json: str, *, bucket: str) -> str:
data = DocumentInput.model_validate_json(arguments_json)
result = await inspect_document(client, data, bucket=bucket)
return result.model_dump_json()
input_schema = DocumentInput.model_json_schema()
output_schema = DocumentOutput.model_json_schema(mode="serialization")
The application supplies the client and bucket. The agent supplies only the
arguments described by DocumentInput. Pass the generated schema using your
agent framework's tool registration format; supported JSON Schema features vary
by framework and model.
run_tool() validates the JSON before making a request, awaits the operation,
validates the response, and returns JSON. strict=True disables coercions such as
converting a string to an integer. extra="forbid" rejects unexpected input
fields. Generated rboto method signatures still check the explicit bucket and
key arguments when you run mypy or Pyright.
from_attributes=True lets Pydantic read the selected attributes directly from
the native response. You do not need to call to_dict() first. Validation creates
a separate Pydantic model; it does not change the response. Only fields selected
by your model are requested, but reading a selected collection still materializes
that collection.
content_length is optional in the service response. This application's output
requires a nonnegative integer, so an omitted size causes a Pydantic
ValidationError. Use int | None = None if omission is acceptable. Defaults
apply to missing attributes; they do not replace an explicit None.
Service failures keep their rboto exception types. Handle those separately from input or output validation errors. See Errors.
Nested responses
Configure attribute validation on each nested model:
from datetime import datetime
from pydantic import BaseModel, ConfigDict, Field
from rboto import S3Client
class Bucket(BaseModel):
model_config = ConfigDict(from_attributes=True, strict=True)
name: str
creation_date: datetime | None = Field(default=None, strict=False)
class BucketPage(BaseModel):
model_config = ConfigDict(from_attributes=True, strict=True)
buckets: list[Bucket] | None = None
continuation_token: str | None = None
async def list_bucket_page(client: S3Client) -> BucketPage:
response = await client.list_buckets()
return BucketPage.model_validate(response)
This reads native bucket objects inside the native response. A bucket without a
name fails validation because this application's Bucket requires one.
BucketPage describes one response page; pagination remains explicit.
rboto exposes timestamps as strings. The creation_date field explicitly enables
conversion to datetime with Field(strict=False). Keep a str annotation if
you want to retain the original representation.
Standard dataclasses
Dataclasses work without Pydantic. Construct them from the response attributes you want to retain:
from dataclasses import dataclass
from rboto import S3Client
@dataclass(frozen=True, slots=True)
class DocumentInput:
key: str
@dataclass(frozen=True, slots=True)
class DocumentOutput:
content_length: int
content_type: str | None = None
async def inspect_document(
client: S3Client,
data: DocumentInput,
*,
bucket: str,
) -> DocumentOutput:
response = await client.head_object(bucket=bucket, key=data.key)
if response.content_length is None:
raise ValueError("The response did not include an object size")
return DocumentOutput(
content_length=response.content_length,
content_type=response.content_type,
)
Standard dataclass annotations do not validate values at runtime.
frozen=True prevents assigning to fields after construction.
If you need JSON validation and a schema for a dataclass, add Pydantic's
TypeAdapter:
from dataclasses import dataclass
from typing import Annotated
from pydantic import Field, TypeAdapter
@dataclass(frozen=True, slots=True)
class ToolInput:
key: Annotated[str, Field(strict=True, min_length=1)]
# Construct once and reuse the adapter across tool calls.
input_adapter = TypeAdapter(ToolInput)
input_schema = input_adapter.json_schema()
def parse_arguments(arguments_json: str) -> ToolInput:
return input_adapter.validate_json(arguments_json)
The adapter returns a ToolInput instance. Validation runs when you call the
adapter; calling the standard dataclass constructor directly bypasses it.
For native responses, construct the dataclass explicitly as above, or validate a
dictionary with an adapter. Attribute validation for BaseModel is a separate
path from dataclass validation.
For a complete handler that validates an event and returns JSON-compatible data, see AWS Lambda.
Structured model responses
For an agent using Bedrock, extract the JSON text produced by the model and pass
it to your application's BaseModel.model_validate_json() or
TypeAdapter.validate_json(). The Bedrock response envelope has a different
structure from that application payload.
Validate returned data even when you supplied a schema to the model. Choosing tools, handling validation failures, and retrying model generation belong in the application or agent framework.
Streaming responses
Select metadata fields when validating a streaming response. Leave the body or
event receiver out of the Pydantic model, then consume it explicitly with
await response.body.read() or async iteration.
Validating metadata does not read the body. Streams are consumable state and do not become JSON through model validation. See Streaming.