Skip to content

Commit d1dd64f

Browse files
authored
opentelemetry: add span event for instructions (#1529)
1 parent 1c009f3 commit d1dd64f

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

pydantic_ai_slim/pydantic_ai/models/instrumented.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,11 @@ def event_to_dict(event: Event) -> dict[str, Any]:
261261
@staticmethod
262262
def messages_to_otel_events(messages: list[ModelMessage]) -> list[Event]:
263263
events: list[Event] = []
264+
last_model_request: ModelRequest | None = None
264265
for message_index, message in enumerate(messages):
265266
message_events: list[Event] = []
266267
if isinstance(message, ModelRequest):
268+
last_model_request = message
267269
for part in message.parts:
268270
if hasattr(part, 'otel_event'):
269271
message_events.append(part.otel_event())
@@ -275,6 +277,10 @@ def messages_to_otel_events(messages: list[ModelMessage]) -> list[Event]:
275277
**(event.attributes or {}),
276278
}
277279
events.extend(message_events)
280+
if last_model_request and last_model_request.instructions:
281+
events.insert(
282+
0, Event('gen_ai.system.message', body={'content': last_model_request.instructions, 'role': 'system'})
283+
)
278284
for event in events:
279285
event.body = InstrumentedModel.serialize_any(event.body)
280286
return events

tests/models/test_instrumented.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,3 +692,47 @@ def __repr__(self):
692692
'event.name': 'gen_ai.tool.message',
693693
},
694694
]
695+
696+
697+
def test_messages_to_otel_events_instructions():
698+
messages = [
699+
ModelRequest(instructions='instructions', parts=[UserPromptPart('user_prompt')]),
700+
ModelResponse(parts=[TextPart('text1')]),
701+
]
702+
assert [
703+
InstrumentedModel.event_to_dict(e) for e in InstrumentedModel.messages_to_otel_events(messages)
704+
] == snapshot(
705+
[
706+
{'content': 'instructions', 'role': 'system', 'event.name': 'gen_ai.system.message'},
707+
{'content': 'user_prompt', 'role': 'user', 'gen_ai.message.index': 0, 'event.name': 'gen_ai.user.message'},
708+
{
709+
'role': 'assistant',
710+
'content': 'text1',
711+
'gen_ai.message.index': 1,
712+
'event.name': 'gen_ai.assistant.message',
713+
},
714+
]
715+
)
716+
717+
718+
def test_messages_to_otel_events_instructions_multiple_messages():
719+
messages = [
720+
ModelRequest(instructions='instructions', parts=[UserPromptPart('user_prompt')]),
721+
ModelResponse(parts=[TextPart('text1')]),
722+
ModelRequest(instructions='instructions2', parts=[UserPromptPart('user_prompt2')]),
723+
]
724+
assert [
725+
InstrumentedModel.event_to_dict(e) for e in InstrumentedModel.messages_to_otel_events(messages)
726+
] == snapshot(
727+
[
728+
{'content': 'instructions2', 'role': 'system', 'event.name': 'gen_ai.system.message'},
729+
{'content': 'user_prompt', 'role': 'user', 'gen_ai.message.index': 0, 'event.name': 'gen_ai.user.message'},
730+
{
731+
'role': 'assistant',
732+
'content': 'text1',
733+
'gen_ai.message.index': 1,
734+
'event.name': 'gen_ai.assistant.message',
735+
},
736+
{'content': 'user_prompt2', 'role': 'user', 'gen_ai.message.index': 2, 'event.name': 'gen_ai.user.message'},
737+
]
738+
)

0 commit comments

Comments
 (0)