Skip to content

Commit

Permalink
feat(prompt): indent support
Browse files Browse the repository at this point in the history
  • Loading branch information
mszenfeld committed Dec 24, 2024
1 parent 9e0ff3a commit 205f2fb
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 10 deletions.
27 changes: 22 additions & 5 deletions pydantic_ai_slim/pydantic_ai/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,29 +189,46 @@ def __init__(self, tag: str, content: Any):
self._tag = tag
self._content = prepare_content(content)

def build(self) -> str:
def build(self, indent: bool = True) -> str:
"""Build the XML string representation of the content.
Args:
indent: If True, format the output with 2-space indentation.
Default is True.
Returns:
A string containing the XML representation of the content.
Special characters are properly escaped, and boolean values are
converted to lowercase 'true'/'false'.
Examples:
>>> XMLTagBuilder('user', {'name': 'John'}).build()
'<user><name>John</name></user>'
>>> print(XMLTagBuilder('user', {'name': 'John'}).build(indent=False))
<user><name>John</name></user>
>>> print(XMLTagBuilder('user', {'name': 'John'}).build())
<user>
<name>John</name>
</user>
>>> XMLTagBuilder('items', [1, 2]).build()
'<items>1</items><items>2</items>'
>>> print(XMLTagBuilder('items', [1, 2]).build())
<items>1</items>
<items>2</items>
"""
elements = self._build_element(self._tag, self._content)

if isinstance(elements, list):
if indent:
return '\n'.join(
ET.indent(element, space=' ') or ET.tostring(element, encoding='unicode', method='xml').strip()
for element in cast(list[ET.Element], elements)
)
return ''.join(
ET.tostring(element, encoding='unicode', method='xml').strip()
for element in cast(list[ET.Element], elements)
)

if indent:
ET.indent(elements, space=' ')
return ET.tostring(elements, encoding='unicode', method='xml').strip()

def _build_element(self, tag: str, content: Content) -> XMLContent:
Expand Down
41 changes: 36 additions & 5 deletions tests/test_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ class TestXMLContentFormatting:
)
def test_simple_schema(self, content: Any) -> None:
builder = XMLTagBuilder('context', content)
got = builder.build()
got = builder.build(indent=False)

assert got == '<context><age>42</age><location><country>UK</country><city>London</city></location></context>'

Expand All @@ -256,7 +256,7 @@ def test_simple_schema(self, content: Any) -> None:
)
def test_list(self, content: Any) -> None:
builder = XMLTagBuilder('examples', content)
got = builder.build()
got = builder.build(indent=False)

assert (
got
Expand All @@ -268,23 +268,54 @@ def test_dict_with_list(self) -> None:
'context',
{'users': ['John', 'Jane']},
)
got = builder.build()
got = builder.build(indent=False)

assert got == '<context><users>John</users><users>Jane</users></context>'

def test_str(self) -> None:
rules = ['Rule #1', 'Rule #2']
builder = XMLTagBuilder('rules', '\n'.join(f'- {rule}' for rule in rules))
got = builder.build()
got = builder.build(indent=False)

assert got == '<rules>- Rule #1\n- Rule #2</rules>'

def test_escaping(self) -> None:
builder = XMLTagBuilder('user', {'name': '</name>John & Jane', 'age': 30})
got = builder.build()
got = builder.build(indent=False)

assert got == '<user><name>&lt;/name&gt;John &amp; Jane</name><age>30</age></user>'

def test_indent(self) -> None:
builder = XMLTagBuilder('user', {'name': 'John', 'age': 30})
got = builder.build(indent=True)

assert got == '\n'.join(
line
for line in (
'<user>',
' <name>John</name>',
' <age>30</age>',
'</user>',
)
)

def test_indent_list(self) -> None:
builder = XMLTagBuilder(
'context',
{'users': ['John', 'Jane']},
)
got = builder.build(indent=True)

assert got == '\n'.join(
line
for line in (
'<context>',
' <users>John</users>',
' <users>Jane</users>',
'</context>',
)
)


@pytest.mark.parametrize(
['tag', 'function'],
Expand Down

0 comments on commit 205f2fb

Please sign in to comment.