Skip to content

Comments

Feature: BodyPart Transformer#94

Open
K1rL3s wants to merge 7 commits intoreagento:developfrom
K1rL3s:feature/body-part
Open

Feature: BodyPart Transformer#94
K1rL3s wants to merge 7 commits intoreagento:developfrom
K1rL3s:feature/body-part

Conversation

@K1rL3s
Copy link

@K1rL3s K1rL3s commented Jan 25, 2026

Feature: Assemble Request Body from Arguments

This PR adds the ability to construct a request body from multiple, individual function arguments.

Problem Solved

The main goal is to avoid needing a separate dataclass for simple request bodies.

Before, creating a request body required passing a single model object. This is inconvenient for APIs that expect a flat JSON object, as it forces the creation of a new class just for the body schema. This PR allows defining body fields directly as function arguments.

Implementation Details

The implementation uses two new request transformers: BodyPart and BodyPartDump.

BodyPart Transformer

  • The BodyPart("arg_name") transformer maps a function argument to a field in the request body.
  • It can be applied to multiple arguments in the same function.
  • During request processing, each BodyPart adds its key-value pair to the request.body dictionary.

BodyPartDump Transformer

  • The RestBuilder and JsonRPCBuilder automatically add the BodyPartDump transformer when BodyPart arguments are detected.
  • It serializes the dictionary of body parts using a type-aware dumper (like adaptix.Retort).
  • To provide type context to the dumper, it dynamically creates a temporary dataclass from the collected BodyPart arguments and their types.
  • This enables the dumper to perform operations like name style conversion (e.g., snake_case to camelCase).

Example

rest = RestBuilder(
    request_body_dumper=Retort(
        recipe=[name_mapping(name_style=NameStyle.CAMEL)]
    ),
)

@rest.post(
    "todos",
    BodyPart("id"),
    BodyPart("user_id"),
    BodyPart("title"),
)
def create_todo(self, id: int, user_id: int, title: str) -> Todo:
    pass

# Calling client.create_todo(id=1, user_id=99, title="Test")
# sends the following JSON body:
# {
#   "id": 1,
#   "userId": 99,
#   "title": "Test"
# }

@K1rL3s
Copy link
Author

K1rL3s commented Jan 25, 2026

This copy paste can be removed if define BuilderParams.request_body_dumper as a RequestTransformer. This is what I originally did before adding the _get_body_part_fields(spec) method and the condition for self._get_body_part_fields(spec). But this breaks backward compatibility, so it is as it is.

image image

@K1rL3s
Copy link
Author

K1rL3s commented Jan 25, 2026

I'm not sure if this is the best way to implement BodyPartDump.transform_request, and I don't know how it affects performance.
And I think it is possible to generate the name of a stub dataclass depending on the name of the method.

image

for f in fields_out
if f.dest == FieldDestination.BODY_PART
}
stub_dataclass = make_dataclass("StubDataclass", types.items())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think it is a good idea to create class inside method. Can't we just dump each field separately?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can, but then name_mapping from adaptix.Retort wont work on field names. I dont know how to implement this with a field-value dictionary and a field-type dictionary without creating a dataclass in place

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not need name_mapping here as we explicitly set field name in BodyPart()

- Split the body of `JsonRPCBuilder._add_default_request_body_transformers` into several methods
- Same with `RestBuilder._add_default_request_body_transformers`
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
3.1% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

),
]
return []
class BodyPart(DestTransformer):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather call it BodyField so not to mix up with other parts like files or chunks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants