6565_InputHandler = Callable [[Any , bool ], _InputHandlerResult ]
6666
6767
68- def _select_input_handler (item_type : Any ) -> _InputHandler :
69- """
70- Select an input handler for a Responses input item.
71-
72- We avoid enumerating every OpenAI `type` value. Instead we:
73- - Handle a few semantically special types explicitly.
74- - Route tool requests/results by naming patterns (`*_call`, `*_call_output`, etc.).
75- - Fall back to `_handle_generic_input` for unknown/future types.
76- """
77- handler : _InputHandler = _handle_generic_input
78- if not isinstance (item_type , str ) or not item_type :
79- return handler
80-
81- # Explicit "special" types where we want stable, structured bodies.
82- if item_type == "message" :
83- handler = _handle_message_input
84- elif item_type == "function_call" :
85- handler = _handle_function_tool_call
86- elif item_type == "mcp_call" :
87- handler = _handle_mcp_call
88- elif item_type == "reasoning" :
89- handler = _handle_reasoning_item
90- elif item_type == "image_generation_call" :
91- handler = _handle_image_generation_call
92- # Tool call results sent back to the model.
93- elif (
94- item_type .endswith ("_call_output" )
95- or item_type .endswith ("_output" )
96- or item_type .endswith ("_response" )
97- ):
98- handler = _handle_tool_call_output_input
99- # Tool calls requested by the model.
100- elif item_type .endswith ("_call" ) or item_type .startswith ("mcp_" ):
101- handler = _handle_tool_call_input
102-
103- return handler
68+ def _put (body : dict [str , Any ], key : str , value : Any ) -> None :
69+ """Set `body[key] = value` only when value is meaningfully present."""
70+ if value is None :
71+ return
72+ if isinstance (value , str ) and not value :
73+ return
74+ body [key ] = value
10475
10576
10677def _create_log_record (
@@ -113,17 +84,26 @@ def _create_log_record(
11384 )
11485
11586
87+ def _tool_item_id (value : Any ) -> Any :
88+ return get_property_value (value , "call_id" ) or get_property_value (value , "id" )
89+
90+
91+ def _tool_item_base_body (
92+ value : Any , * , include_name : bool = False , include_type : bool = False
93+ ) -> _InputBody :
94+ body : _InputBody = {}
95+ _put (body , "id" , _tool_item_id (value ))
96+ if include_name :
97+ _put (body , "name" , get_property_value (value , "name" ))
98+ if include_type :
99+ _put (body , "type" , get_property_value (value , "type" ))
100+ return body
101+
102+
116103def responses_input_to_event (
117104 input_value : str | ResponseInputItemParam , capture_content : bool
118105) -> LogRecord | None :
119- """
120- Convert a single Responses API input item to an input log event.
121-
122- Flow:
123- - Determine the item "type"
124- - Dispatch to a handler that extracts a minimal body + role
125- - Build a `gen_ai.<role>.input` `LogRecord`
126- """
106+ """Convert one Responses input item into a `gen_ai.<role>.input` `LogRecord`."""
127107 if isinstance (input_value , str ):
128108 body = {"content" : input_value } if capture_content else {}
129109 return _create_log_record (
@@ -132,11 +112,44 @@ def responses_input_to_event(
132112 )
133113
134114 item_type = get_property_value (input_value , "type" )
135- handler = _select_input_handler (item_type )
115+
116+ # Pattern-based routing avoids having to enumerate every OpenAI `type` string.
117+ handler : _InputHandler = _input_generic
118+ if isinstance (item_type , str ) and item_type :
119+ if item_type == "message" :
120+ handler = _input_message
121+ elif item_type == "function_call" :
122+ handler = _input_function_tool_call
123+ elif item_type == "mcp_call" :
124+ handler = _input_mcp_call
125+ elif item_type == "reasoning" :
126+ handler = _input_reasoning
127+ elif item_type == "image_generation_call" :
128+ handler = _input_image_generation_call
129+ elif (
130+ item_type .endswith ("_call_output" )
131+ or item_type .endswith ("_output" )
132+ or item_type .endswith ("_response" )
133+ ):
134+ handler = _input_tool_call_output
135+ elif item_type .endswith ("_call" ) or item_type .startswith ("mcp_" ):
136+ handler = _input_tool_call
137+
136138 role , body = handler (input_value , capture_content )
137139 return _create_log_record (_EVENT_NAME_INPUT_FMT .format (role = role ), body )
138140
139141
142+ def output_to_event (
143+ output : ResponseOutputItem , capture_content : bool
144+ ) -> LogRecord | None :
145+ """Convert one Responses output item into a `gen_ai.output` `LogRecord`."""
146+ body = _base_output_body (output )
147+ output_type = getattr (output , "type" , None )
148+ handler = _OUTPUT_TYPE_HANDLERS .get (output_type , _output_unknown_body )
149+ body .update (handler (output , capture_content ))
150+ return _create_log_record (_EVENT_NAME_OUTPUT , body )
151+
152+
140153def _base_output_body (output : ResponseOutputItem ) -> dict [str , Any ]:
141154 body : dict [str , Any ] = {}
142155
@@ -187,9 +200,7 @@ def _output_reasoning_body(
187200) -> dict [str , Any ]:
188201 body : dict [str , Any ] = {"type" : "reasoning" }
189202
190- item_id = getattr (output , "id" , None )
191- if item_id :
192- body ["id" ] = item_id
203+ _put (body , "id" , getattr (output , "id" , None ))
193204
194205 if capture_content :
195206 summary = getattr (output , "summary" , None )
@@ -207,12 +218,8 @@ def _output_tool_call_body(
207218 body : dict [str , Any ] = {"type" : output_type }
208219
209220 call_id = getattr (output , "call_id" , None ) or getattr (output , "id" , None )
210- if call_id :
211- body ["id" ] = call_id
212-
213- name = getattr (output , "name" , None )
214- if name :
215- body ["name" ] = name
221+ _put (body , "id" , call_id )
222+ _put (body , "name" , getattr (output , "name" , None ))
216223
217224 return body
218225
@@ -228,35 +235,12 @@ def _output_unknown_body(
228235 "message" : _output_message_body ,
229236 "function_call" : _output_function_call_body ,
230237 "reasoning" : _output_reasoning_body ,
238+ ** {t : _output_tool_call_body for t in _TOOL_CALL_OUTPUT_TYPES },
231239}
232240
233-
234- def output_to_event (
235- output : ResponseOutputItem , capture_content : bool
236- ) -> LogRecord | None :
237- """
238- Convert a single Responses API output item to an output log event.
239-
240- Flow:
241- - Extract common output fields (`index`, `finish_reason`)
242- - Dispatch by `output.type` to a small body builder
243- - Build a `gen_ai.output` `LogRecord`
244- """
245- body = _base_output_body (output )
246- output_type = getattr (output , "type" , None )
247- if output_type in _TOOL_CALL_OUTPUT_TYPES :
248- body .update (_output_tool_call_body (output , capture_content ))
249- else :
250- handler = _OUTPUT_TYPE_HANDLERS .get (output_type , _output_unknown_body )
251- body .update (handler (output , capture_content ))
252-
253- return _create_log_record (_EVENT_NAME_OUTPUT , body )
254-
255-
256- def _handle_message_input (
241+ def _input_message (
257242 input_value : Any , capture_content : bool
258243) -> _InputHandlerResult :
259- """Message: { content: ContentPart[], role, type: "message" }"""
260244 role = get_property_value (input_value , "role" ) or "user"
261245 body : _InputBody = {}
262246
@@ -270,24 +254,13 @@ def _handle_message_input(
270254 return role , body
271255
272256
273- def _handle_mcp_call (
257+ def _input_mcp_call (
274258 input_value : Any , capture_content : bool
275259) -> _InputHandlerResult :
276- """McpCall: { id, name, arguments, output?, type: "mcp_call" }"""
277- body : _InputBody = {}
278-
279- item_id = get_property_value (input_value , "id" )
280- if item_id :
281- body ["id" ] = item_id
282-
283- name = get_property_value (input_value , "name" )
284- if name :
285- body ["name" ] = name
260+ body = _tool_item_base_body (input_value , include_name = True )
286261
287262 if capture_content :
288- arguments = get_property_value (input_value , "arguments" )
289- if arguments :
290- body ["arguments" ] = arguments
263+ _put (body , "arguments" , get_property_value (input_value , "arguments" ))
291264
292265 output = get_property_value (input_value , "output" )
293266 if output and isinstance (output , str ):
@@ -296,10 +269,9 @@ def _handle_mcp_call(
296269 return "tool" , body
297270
298271
299- def _handle_function_tool_call (
272+ def _input_function_tool_call (
300273 input_value : Any , capture_content : bool
301274) -> _InputHandlerResult :
302- """FunctionToolCall: { call_id, name, arguments, type: "function_call" }"""
303275 body : _InputBody = {}
304276
305277 call_id = get_property_value (input_value , "call_id" )
@@ -321,15 +293,11 @@ def _handle_function_tool_call(
321293 return "assistant" , body
322294
323295
324- def _handle_reasoning_item (
296+ def _input_reasoning (
325297 input_value : Any , capture_content : bool
326298) -> _InputHandlerResult :
327- """ReasoningItem: { id, summary: ContentPart[], type: "reasoning" }"""
328299 body : _InputBody = {}
329-
330- item_id = get_property_value (input_value , "id" )
331- if item_id :
332- body ["id" ] = item_id
300+ _put (body , "id" , get_property_value (input_value , "id" ))
333301
334302 if capture_content :
335303 summary = get_property_value (input_value , "summary" )
@@ -341,53 +309,23 @@ def _handle_reasoning_item(
341309 return "assistant" , body
342310
343311
344- def _handle_tool_call_input (
312+ def _input_tool_call (
345313 input_value : Any , capture_content : bool
346314) -> _InputHandlerResult :
347- """Generic handler for tool call inputs (file_search, web_search, computer, code_interpreter, etc.)."""
348- body : _InputBody = {}
349-
350- call_id = get_property_value (input_value , "call_id" ) or get_property_value (
351- input_value , "id"
352- )
353- if call_id :
354- body ["id" ] = call_id
355-
356- name = get_property_value (input_value , "name" )
357- if name :
358- body ["name" ] = name
359-
360- item_type = get_property_value (input_value , "type" )
361- if item_type :
362- body ["type" ] = item_type
315+ body = _tool_item_base_body (input_value , include_name = True , include_type = True )
363316
364317 if capture_content :
365- query = get_property_value (input_value , "query" )
366- if query :
367- body ["query" ] = query
368-
369- arguments = get_property_value (input_value , "arguments" )
370- if arguments :
371- body ["arguments" ] = arguments
372-
373- code = get_property_value (input_value , "code" )
374- if code :
375- body ["content" ] = code
318+ _put (body , "query" , get_property_value (input_value , "query" ))
319+ _put (body , "arguments" , get_property_value (input_value , "arguments" ))
320+ _put (body , "content" , get_property_value (input_value , "code" ))
376321
377322 return "assistant" , body
378323
379324
380- def _handle_tool_call_output_input (
325+ def _input_tool_call_output (
381326 input_value : Any , capture_content : bool
382327) -> _InputHandlerResult :
383- """Generic handler for tool call output inputs (tool results sent back to the model)."""
384- body : _InputBody = {}
385-
386- call_id = get_property_value (input_value , "call_id" ) or get_property_value (
387- input_value , "id"
388- )
389- if call_id :
390- body ["id" ] = call_id
328+ body = _tool_item_base_body (input_value )
391329
392330 if capture_content :
393331 output = get_property_value (input_value , "output" )
@@ -406,19 +344,12 @@ def _handle_tool_call_output_input(
406344 return "tool" , body
407345
408346
409- def _handle_image_generation_call (
347+ def _input_image_generation_call (
410348 input_value : Any , capture_content : bool
411349) -> _InputHandlerResult :
412- """ImageGenerationCall: { id, result, status, type: "image_generation_call" }"""
413350 body : _InputBody = {}
414-
415- item_id = get_property_value (input_value , "id" )
416- if item_id :
417- body ["id" ] = item_id
418-
419- status = get_property_value (input_value , "status" )
420- if status :
421- body ["status" ] = status
351+ _put (body , "id" , get_property_value (input_value , "id" ))
352+ _put (body , "status" , get_property_value (input_value , "status" ))
422353
423354 if capture_content :
424355 result = get_property_value (input_value , "result" )
@@ -428,22 +359,13 @@ def _handle_image_generation_call(
428359 return "assistant" , body
429360
430361
431- def _handle_generic_input (
362+ def _input_generic (
432363 input_value : Any , capture_content : bool
433364) -> _InputHandlerResult :
434- """Fallback handler for unknown input types."""
435365 role = get_property_value (input_value , "role" ) or "user"
436366 body : _InputBody = {}
437-
438- item_id = get_property_value (input_value , "id" ) or get_property_value (
439- input_value , "call_id"
440- )
441- if item_id :
442- body ["id" ] = item_id
443-
444- item_type = get_property_value (input_value , "type" )
445- if item_type :
446- body ["type" ] = item_type
367+ _put (body , "id" , get_property_value (input_value , "id" ) or get_property_value (input_value , "call_id" ))
368+ _put (body , "type" , get_property_value (input_value , "type" ))
447369
448370 if capture_content :
449371 content = get_property_value (input_value , "content" )
@@ -459,14 +381,12 @@ def _handle_generic_input(
459381
460382
461383def _extract_text_from_content_parts (content_parts ) -> str | None :
462- """Extract text content from a list of content parts."""
463384 if not content_parts :
464385 return None
465386
466387 if isinstance (content_parts , str ):
467388 return content_parts
468389
469- # Handle non-iterable types gracefully
470390 if not isinstance (content_parts , (list , tuple )):
471391 return None
472392
0 commit comments