@@ -178,17 +178,20 @@ def __init__(
178178 description = 'Monetary cost' ,
179179 )
180180
181- def messages_to_otel_events (self , messages : list [ModelMessage ]) -> list [Event ]:
181+ def messages_to_otel_events (
182+ self , messages : list [ModelMessage ], parameters : ModelRequestParameters | None = None
183+ ) -> list [Event ]:
182184 """Convert a list of model messages to OpenTelemetry events.
183185
184186 Args:
185187 messages: The messages to convert.
188+ parameters: The model request parameters.
186189
187190 Returns:
188191 A list of OpenTelemetry events.
189192 """
190193 events : list [Event ] = []
191- instructions = InstrumentedModel ._get_instructions (messages ) # pyright: ignore [reportPrivateUsage]
194+ instructions = InstrumentedModel ._get_instructions (messages , parameters ) # pyright: ignore [reportPrivateUsage]
192195 if instructions is not None :
193196 events .append (
194197 Event (
@@ -235,10 +238,17 @@ def messages_to_otel_messages(self, messages: list[ModelMessage]) -> list[_otel_
235238 result .append (otel_message )
236239 return result
237240
238- def handle_messages (self , input_messages : list [ModelMessage ], response : ModelResponse , system : str , span : Span ):
241+ def handle_messages (
242+ self ,
243+ input_messages : list [ModelMessage ],
244+ response : ModelResponse ,
245+ system : str ,
246+ span : Span ,
247+ parameters : ModelRequestParameters | None = None ,
248+ ):
239249 if self .version == 1 :
240- events = self .messages_to_otel_events (input_messages )
241- for event in self .messages_to_otel_events ([response ]):
250+ events = self .messages_to_otel_events (input_messages , parameters )
251+ for event in self .messages_to_otel_events ([response ], parameters ):
242252 events .append (
243253 Event (
244254 'gen_ai.choice' ,
@@ -258,7 +268,7 @@ def handle_messages(self, input_messages: list[ModelMessage], response: ModelRes
258268 output_messages = self .messages_to_otel_messages ([response ])
259269 assert len (output_messages ) == 1
260270 output_message = output_messages [0 ]
261- instructions = InstrumentedModel ._get_instructions (input_messages ) # pyright: ignore [reportPrivateUsage]
271+ instructions = InstrumentedModel ._get_instructions (input_messages , parameters ) # pyright: ignore [reportPrivateUsage]
262272 system_instructions_attributes = self .system_instructions_attributes (instructions )
263273 attributes : dict [str , AttributeValue ] = {
264274 'gen_ai.input.messages' : json .dumps (self .messages_to_otel_messages (input_messages )),
@@ -360,7 +370,7 @@ async def request(
360370 )
361371 with self ._instrument (messages , prepared_settings , prepared_parameters ) as finish :
362372 response = await self .wrapped .request (messages , model_settings , model_request_parameters )
363- finish (response )
373+ finish (response , prepared_parameters )
364374 return response
365375
366376 @asynccontextmanager
@@ -384,15 +394,15 @@ async def request_stream(
384394 yield response_stream
385395 finally :
386396 if response_stream : # pragma: no branch
387- finish (response_stream .get ())
397+ finish (response_stream .get (), prepared_parameters )
388398
389399 @contextmanager
390400 def _instrument (
391401 self ,
392402 messages : list [ModelMessage ],
393403 model_settings : ModelSettings | None ,
394404 model_request_parameters : ModelRequestParameters ,
395- ) -> Iterator [Callable [[ModelResponse ], None ]]:
405+ ) -> Iterator [Callable [[ModelResponse , ModelRequestParameters ], None ]]:
396406 operation = 'chat'
397407 span_name = f'{ operation } { self .model_name } '
398408 # TODO Missing attributes:
@@ -401,7 +411,7 @@ def _instrument(
401411 attributes : dict [str , AttributeValue ] = {
402412 'gen_ai.operation.name' : operation ,
403413 ** self .model_attributes (self .wrapped ),
404- 'model_request_parameters' : json . dumps ( InstrumentedModel . serialize_any ( model_request_parameters ) ),
414+ ** self . model_request_parameters_attributes ( model_request_parameters ),
405415 'logfire.json_schema' : json .dumps (
406416 {
407417 'type' : 'object' ,
@@ -419,7 +429,7 @@ def _instrument(
419429 try :
420430 with self .instrumentation_settings .tracer .start_as_current_span (span_name , attributes = attributes ) as span :
421431
422- def finish (response : ModelResponse ):
432+ def finish (response : ModelResponse , parameters : ModelRequestParameters ):
423433 # FallbackModel updates these span attributes.
424434 attributes .update (getattr (span , 'attributes' , {}))
425435 request_model = attributes [GEN_AI_REQUEST_MODEL_ATTRIBUTE ]
@@ -443,7 +453,7 @@ def _record_metrics():
443453 if not span .is_recording ():
444454 return
445455
446- self .instrumentation_settings .handle_messages (messages , response , system , span )
456+ self .instrumentation_settings .handle_messages (messages , response , system , span , parameters )
447457
448458 attributes_to_set = {
449459 ** response .usage .opentelemetry_attributes (),
@@ -476,7 +486,7 @@ def _record_metrics():
476486 record_metrics ()
477487
478488 @staticmethod
479- def model_attributes (model : Model ):
489+ def model_attributes (model : Model ) -> dict [ str , AttributeValue ] :
480490 attributes : dict [str , AttributeValue ] = {
481491 GEN_AI_SYSTEM_ATTRIBUTE : model .system ,
482492 GEN_AI_REQUEST_MODEL_ATTRIBUTE : model .model_name ,
@@ -494,6 +504,12 @@ def model_attributes(model: Model):
494504
495505 return attributes
496506
507+ @staticmethod
508+ def model_request_parameters_attributes (
509+ model_request_parameters : ModelRequestParameters ,
510+ ) -> dict [str , AttributeValue ]:
511+ return {'model_request_parameters' : json .dumps (InstrumentedModel .serialize_any (model_request_parameters ))}
512+
497513 @staticmethod
498514 def event_to_dict (event : Event ) -> dict [str , Any ]:
499515 if not event .body :
0 commit comments