Skip to content

Commit 83c8e01

Browse files
authored
Fixes for excluding content (#2180)
1 parent 01b6b2c commit 83c8e01

File tree

3 files changed

+119
-4
lines changed

3 files changed

+119
-4
lines changed

pydantic_ai_slim/pydantic_ai/messages.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,9 +411,9 @@ class UserPromptPart:
411411
"""Part type identifier, this is available on all parts as a discriminator."""
412412

413413
def otel_event(self, settings: InstrumentationSettings) -> Event:
414-
content: str | list[dict[str, Any] | str]
414+
content: str | list[dict[str, Any] | str] | dict[str, Any]
415415
if isinstance(self.content, str):
416-
content = self.content
416+
content = self.content if settings.include_content else {'kind': 'text'}
417417
else:
418418
content = []
419419
for part in self.content:
@@ -743,7 +743,7 @@ def new_event_body():
743743
'type': 'function', # TODO https://github.com/pydantic/pydantic-ai/issues/888
744744
'function': {
745745
'name': part.tool_name,
746-
'arguments': part.args,
746+
**({'arguments': part.args} if settings.include_content else {}),
747747
},
748748
}
749749
)

tests/models/test_instrumented.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,7 @@ def test_messages_without_content(document_content: BinaryContent):
863863
ModelRequest(parts=[ToolReturnPart('tool', 'tool_return_content', 'tool_call_1')]),
864864
ModelRequest(parts=[RetryPromptPart('retry_prompt', tool_name='tool', tool_call_id='tool_call_2')]),
865865
ModelRequest(parts=[UserPromptPart(content=['user_prompt2', document_content])]),
866+
ModelRequest(parts=[UserPromptPart('simple text prompt')]),
866867
]
867868
settings = InstrumentationSettings(include_content=False)
868869
assert [InstrumentedModel.event_to_dict(e) for e in settings.messages_to_otel_events(messages)] == snapshot(
@@ -896,7 +897,7 @@ def test_messages_without_content(document_content: BinaryContent):
896897
{
897898
'id': IsStr(),
898899
'type': 'function',
899-
'function': {'name': 'my_tool', 'arguments': {'a': 13, 'b': 4}},
900+
'function': {'name': 'my_tool'},
900901
}
901902
],
902903
'gen_ai.message.index': 3,
@@ -922,5 +923,11 @@ def test_messages_without_content(document_content: BinaryContent):
922923
'gen_ai.message.index': 6,
923924
'event.name': 'gen_ai.user.message',
924925
},
926+
{
927+
'content': {'kind': 'text'},
928+
'role': 'user',
929+
'gen_ai.message.index': 7,
930+
'event.name': 'gen_ai.user.message',
931+
},
925932
]
926933
)

tests/test_logfire.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,114 @@ class MyOutput:
415415
)
416416

417417

418+
@pytest.mark.skipif(not logfire_installed, reason='logfire not installed')
419+
def test_instructions_with_structured_output_exclude_content(get_logfire_summary: Callable[[], LogfireSummary]) -> None:
420+
@dataclass
421+
class MyOutput:
422+
content: str
423+
424+
settings: InstrumentationSettings = InstrumentationSettings(include_content=False)
425+
426+
my_agent = Agent(model=TestModel(), instructions='Here are some instructions', instrument=settings)
427+
428+
result = my_agent.run_sync('Hello', output_type=MyOutput)
429+
assert result.output == snapshot(MyOutput(content='a'))
430+
431+
summary = get_logfire_summary()
432+
assert summary.attributes[0] == snapshot(
433+
{
434+
'model_name': 'test',
435+
'agent_name': 'my_agent',
436+
'logfire.msg': 'my_agent run',
437+
'logfire.span_type': 'span',
438+
'gen_ai.usage.input_tokens': 51,
439+
'gen_ai.usage.output_tokens': 5,
440+
'all_messages_events': IsJson(
441+
snapshot(
442+
[
443+
{
444+
'content': 'Here are some instructions',
445+
'role': 'system',
446+
'event.name': 'gen_ai.system.message',
447+
},
448+
{
449+
'content': {'kind': 'text'},
450+
'role': 'user',
451+
'gen_ai.message.index': 0,
452+
'event.name': 'gen_ai.user.message',
453+
},
454+
{
455+
'role': 'assistant',
456+
'tool_calls': [
457+
{
458+
'id': IsStr(),
459+
'type': 'function',
460+
'function': {'name': 'final_result'},
461+
}
462+
],
463+
'gen_ai.message.index': 1,
464+
'event.name': 'gen_ai.assistant.message',
465+
},
466+
{
467+
'role': 'tool',
468+
'id': IsStr(),
469+
'name': 'final_result',
470+
'gen_ai.message.index': 2,
471+
'event.name': 'gen_ai.tool.message',
472+
},
473+
]
474+
)
475+
),
476+
'final_result': '{"content": "a"}',
477+
'logfire.json_schema': IsJson(
478+
snapshot(
479+
{
480+
'type': 'object',
481+
'properties': {'all_messages_events': {'type': 'array'}, 'final_result': {'type': 'object'}},
482+
}
483+
)
484+
),
485+
}
486+
)
487+
chat_span_attributes = summary.attributes[1]
488+
assert chat_span_attributes['events'] == snapshot(
489+
IsJson(
490+
snapshot(
491+
[
492+
{
493+
'content': 'Here are some instructions',
494+
'role': 'system',
495+
'gen_ai.system': 'test',
496+
'event.name': 'gen_ai.system.message',
497+
},
498+
{
499+
'event.name': 'gen_ai.user.message',
500+
'content': {'kind': 'text'},
501+
'role': 'user',
502+
'gen_ai.message.index': 0,
503+
'gen_ai.system': 'test',
504+
},
505+
{
506+
'event.name': 'gen_ai.choice',
507+
'index': 0,
508+
'message': {
509+
'role': 'assistant',
510+
'tool_calls': [
511+
{
512+
'id': IsStr(),
513+
'type': 'function',
514+
'function': {'name': 'final_result'},
515+
}
516+
],
517+
},
518+
'gen_ai.system': 'test',
519+
},
520+
]
521+
)
522+
)
523+
)
524+
525+
418526
def test_instrument_all():
419527
model = TestModel()
420528
agent = Agent()

0 commit comments

Comments
 (0)