@@ -598,6 +598,12 @@ def _get_langfuse_data_from_kwargs(resource: OpenAiDefinition, kwargs: Any) -> A
598598
599599 parsed_n = kwargs .get ("n" , 1 ) if not isinstance (kwargs .get ("n" , 1 ), NotGiven ) else 1
600600
601+ parsed_service_tier = (
602+ kwargs .get ("service_tier" , None )
603+ if not isinstance (kwargs .get ("service_tier" , None ), NotGiven )
604+ else None
605+ )
606+
601607 if resource .type == "embedding" :
602608 parsed_dimensions = (
603609 kwargs .get ("dimensions" , None )
@@ -634,6 +640,9 @@ def _get_langfuse_data_from_kwargs(resource: OpenAiDefinition, kwargs: Any) -> A
634640 if parsed_seed is not None :
635641 modelParameters ["seed" ] = parsed_seed
636642
643+ if parsed_service_tier is not None :
644+ modelParameters ["service_tier" ] = parsed_service_tier
645+
637646 langfuse_prompt = kwargs .get ("langfuse_prompt" , None )
638647
639648 return {
@@ -657,6 +666,7 @@ def _create_langfuse_update(
657666 model : Optional [str ] = None ,
658667 usage : Optional [Any ] = None ,
659668 metadata : Optional [Any ] = None ,
669+ model_parameters : Optional [Any ] = None ,
660670) -> Any :
661671 update = {
662672 "output" : completion ,
@@ -668,6 +678,9 @@ def _create_langfuse_update(
668678 if metadata is not None :
669679 update ["metadata" ] = metadata
670680
681+ if model_parameters is not None :
682+ update ["model_parameters" ] = model_parameters
683+
671684 if usage is not None :
672685 update ["usage_details" ] = _parse_usage (usage )
673686 update ["cost_details" ] = _parse_cost (usage )
@@ -721,7 +734,7 @@ def _parse_cost(usage: Optional[Any] = None) -> Any:
721734
722735
723736def _extract_streamed_response_api_response (chunks : Any ) -> Any :
724- completion , model , usage = None , None , None
737+ completion , model , usage , service_tier = None , None , None , None
725738 metadata = {}
726739
727740 for raw_chunk in chunks :
@@ -731,6 +744,7 @@ def _extract_streamed_response_api_response(chunks: Any) -> Any:
731744
732745 response = raw_response .__dict__
733746 model = response .get ("model" )
747+ service_tier = response .get ("service_tier" , None ) or service_tier
734748
735749 for key , val in response .items ():
736750 if key not in ["created_at" , "model" , "output" , "usage" , "text" ]:
@@ -739,18 +753,19 @@ def _extract_streamed_response_api_response(chunks: Any) -> Any:
739753 if key == "output" :
740754 completion = _extract_response_api_completion (val )
741755
742- return (model , completion , usage , metadata )
756+ return (model , completion , usage , metadata , service_tier )
743757
744758
745759def _extract_streamed_openai_response (resource : Any , chunks : Any ) -> Any :
746760 completion : Any = defaultdict (lambda : None ) if resource .type == "chat" else ""
747- model , usage , finish_reason = None , None , None
761+ model , usage , finish_reason , service_tier = None , None , None , None
748762
749763 for chunk in chunks :
750764 if _is_openai_v1 ():
751765 chunk = chunk .__dict__
752766
753767 model = model or chunk .get ("model" , None ) or None
768+ service_tier = service_tier or chunk .get ("service_tier" , None ) or None
754769 chunk_usage = chunk .get ("usage" , None )
755770 if chunk_usage is not None :
756771 usage = chunk_usage
@@ -884,16 +899,18 @@ def get_response_for_chat() -> Any:
884899 get_response_for_chat () if resource .type == "chat" else completion ,
885900 usage ,
886901 {"finish_reason" : finish_reason } if finish_reason is not None else None ,
902+ service_tier ,
887903 )
888904
889905
890906def _get_langfuse_data_from_default_response (
891907 resource : OpenAiDefinition , response : Any
892908) -> Any :
893909 if response is None :
894- return None , "<NoneType response returned from OpenAI>" , None
910+ return None , "<NoneType response returned from OpenAI>" , None , None
895911
896912 model = response .get ("model" , None ) or None
913+ service_tier = response .get ("service_tier" , None ) or None
897914
898915 completion = None
899916
@@ -942,7 +959,23 @@ def _get_langfuse_data_from_default_response(
942959
943960 usage = _parse_usage (response .get ("usage" , None ))
944961
945- return (model , completion , usage )
962+ return (model , completion , usage , service_tier )
963+
964+
965+ def _merge_service_tier_into_model_parameters (
966+ model_parameters : Optional [Any ], service_tier : Optional [Any ]
967+ ) -> Optional [Any ]:
968+ """Merge the response-side service tier into the request-side model parameters.
969+
970+ The response value is authoritative because OpenAI returns the tier that
971+ actually processed the request (e.g. when the request specified "auto").
972+ Returns None when there is nothing to update so callers can skip the
973+ update and keep the request-side model parameters untouched.
974+ """
975+ if service_tier is None :
976+ return None
977+
978+ return {** (model_parameters or {}), "service_tier" : service_tier }
946979
947980
948981def _is_openai_v1 () -> bool :
@@ -999,9 +1032,10 @@ def _finalize_stream_response(
9991032 items : list [Any ],
10001033 generation : LangfuseGeneration ,
10011034 completion_start_time : Optional [datetime ],
1035+ model_parameters : Optional [Any ] = None ,
10021036) -> None :
10031037 try :
1004- model , completion , usage , metadata = (
1038+ model , completion , usage , metadata , service_tier = (
10051039 _extract_streamed_response_api_response (items )
10061040 if resource .object == "Responses" or resource .object == "AsyncResponses"
10071041 else _extract_streamed_openai_response (resource , items )
@@ -1014,6 +1048,9 @@ def _finalize_stream_response(
10141048 model = model ,
10151049 usage = usage ,
10161050 metadata = metadata ,
1051+ model_parameters = _merge_service_tier_into_model_parameters (
1052+ model_parameters , service_tier
1053+ ),
10171054 )
10181055 except Exception :
10191056 pass
@@ -1026,12 +1063,14 @@ def _instrument_openai_stream(
10261063 resource : OpenAiDefinition ,
10271064 response : Any ,
10281065 generation : LangfuseGeneration ,
1066+ model_parameters : Optional [Any ] = None ,
10291067) -> Any :
10301068 if not hasattr (response , "_iterator" ):
10311069 return LangfuseResponseGeneratorSync (
10321070 resource = resource ,
10331071 response = response ,
10341072 generation = generation ,
1073+ model_parameters = model_parameters ,
10351074 )
10361075
10371076 items : list [Any ] = []
@@ -1051,6 +1090,7 @@ def finalize_once() -> None:
10511090 items = items ,
10521091 generation = generation ,
10531092 completion_start_time = completion_start_time ,
1093+ model_parameters = model_parameters ,
10541094 )
10551095
10561096 response ._langfuse_finalize_once = finalize_once # type: ignore[attr-defined]
@@ -1085,12 +1125,14 @@ def _instrument_openai_async_stream(
10851125 resource : OpenAiDefinition ,
10861126 response : Any ,
10871127 generation : LangfuseGeneration ,
1128+ model_parameters : Optional [Any ] = None ,
10881129) -> Any :
10891130 if not hasattr (response , "_iterator" ):
10901131 return LangfuseResponseGeneratorAsync (
10911132 resource = resource ,
10921133 response = response ,
10931134 generation = generation ,
1135+ model_parameters = model_parameters ,
10941136 )
10951137
10961138 items : list [Any ] = []
@@ -1110,6 +1152,7 @@ async def finalize_once() -> None:
11101152 items = items ,
11111153 generation = generation ,
11121154 completion_start_time = completion_start_time ,
1155+ model_parameters = model_parameters ,
11131156 )
11141157
11151158 response ._langfuse_finalize_once = finalize_once # type: ignore[attr-defined]
@@ -1243,21 +1286,25 @@ def _wrap(
12431286 resource = open_ai_resource ,
12441287 response = openai_response ,
12451288 generation = generation ,
1289+ model_parameters = langfuse_data .get ("model_parameters" , None ),
12461290 )
12471291 elif _is_streaming_response (openai_response ):
12481292 return LangfuseResponseGeneratorSync (
12491293 resource = open_ai_resource ,
12501294 response = openai_response ,
12511295 generation = generation ,
1296+ model_parameters = langfuse_data .get ("model_parameters" , None ),
12521297 )
12531298
12541299 else :
12551300 parsed_response = _unwrap_raw_response (openai_response )
1256- model , completion , usage = _get_langfuse_data_from_default_response (
1257- open_ai_resource ,
1258- (parsed_response and parsed_response .__dict__ )
1259- if _is_openai_v1 ()
1260- else parsed_response ,
1301+ model , completion , usage , service_tier = (
1302+ _get_langfuse_data_from_default_response (
1303+ open_ai_resource ,
1304+ (parsed_response and parsed_response .__dict__ )
1305+ if _is_openai_v1 ()
1306+ else parsed_response ,
1307+ )
12611308 )
12621309
12631310 generation .update (
@@ -1267,6 +1314,9 @@ def _wrap(
12671314 cost_details = _parse_cost (parsed_response .usage )
12681315 if hasattr (parsed_response , "usage" )
12691316 else None ,
1317+ model_parameters = _merge_service_tier_into_model_parameters (
1318+ langfuse_data .get ("model_parameters" , None ), service_tier
1319+ ),
12701320 ).end ()
12711321
12721322 return openai_response
@@ -1325,21 +1375,25 @@ async def _wrap_async(
13251375 resource = open_ai_resource ,
13261376 response = openai_response ,
13271377 generation = generation ,
1378+ model_parameters = langfuse_data .get ("model_parameters" , None ),
13281379 )
13291380 elif _is_streaming_response (openai_response ):
13301381 return LangfuseResponseGeneratorAsync (
13311382 resource = open_ai_resource ,
13321383 response = openai_response ,
13331384 generation = generation ,
1385+ model_parameters = langfuse_data .get ("model_parameters" , None ),
13341386 )
13351387
13361388 else :
13371389 parsed_response = _unwrap_raw_response (openai_response )
1338- model , completion , usage = _get_langfuse_data_from_default_response (
1339- open_ai_resource ,
1340- (parsed_response and parsed_response .__dict__ )
1341- if _is_openai_v1 ()
1342- else parsed_response ,
1390+ model , completion , usage , service_tier = (
1391+ _get_langfuse_data_from_default_response (
1392+ open_ai_resource ,
1393+ (parsed_response and parsed_response .__dict__ )
1394+ if _is_openai_v1 ()
1395+ else parsed_response ,
1396+ )
13431397 )
13441398 generation .update (
13451399 model = model ,
@@ -1349,6 +1403,9 @@ async def _wrap_async(
13491403 cost_details = _parse_cost (parsed_response .usage )
13501404 if hasattr (parsed_response , "usage" )
13511405 else None ,
1406+ model_parameters = _merge_service_tier_into_model_parameters (
1407+ langfuse_data .get ("model_parameters" , None ), service_tier
1408+ ),
13521409 ).end ()
13531410
13541411 return openai_response
@@ -1397,12 +1454,14 @@ def __init__(
13971454 resource : Any ,
13981455 response : Any ,
13991456 generation : Any ,
1457+ model_parameters : Optional [Any ] = None ,
14001458 ) -> None :
14011459 self .items : list [Any ] = []
14021460
14031461 self .resource = resource
14041462 self .response = response
14051463 self .generation = generation
1464+ self .model_parameters = model_parameters
14061465 self .completion_start_time : Optional [datetime ] = None
14071466 self ._is_finalized = False
14081467
@@ -1458,6 +1517,7 @@ def _finalize(self) -> None:
14581517 items = self .items ,
14591518 generation = self .generation ,
14601519 completion_start_time = self .completion_start_time ,
1520+ model_parameters = self .model_parameters ,
14611521 )
14621522
14631523
@@ -1468,12 +1528,14 @@ def __init__(
14681528 resource : Any ,
14691529 response : Any ,
14701530 generation : Any ,
1531+ model_parameters : Optional [Any ] = None ,
14711532 ) -> None :
14721533 self .items : list [Any ] = []
14731534
14741535 self .resource = resource
14751536 self .response = response
14761537 self .generation = generation
1538+ self .model_parameters = model_parameters
14771539 self .completion_start_time : Optional [datetime ] = None
14781540 self ._is_finalized = False
14791541
@@ -1520,6 +1582,7 @@ async def _finalize(self) -> None:
15201582 items = self .items ,
15211583 generation = self .generation ,
15221584 completion_start_time = self .completion_start_time ,
1585+ model_parameters = self .model_parameters ,
15231586 )
15241587
15251588 async def close (self ) -> None :
0 commit comments