@@ -90,6 +90,48 @@ class OpenAiDefinition:
9090 type = "chat" ,
9191 sync = True ,
9292 ),
93+ OpenAiDefinition (
94+ module = "openai.resources.images" ,
95+ object = "Images" ,
96+ method = "generate" ,
97+ type = "image" ,
98+ sync = True ,
99+ ),
100+ OpenAiDefinition (
101+ module = "openai.resources.images" ,
102+ object = "AsyncImages" ,
103+ method = "generate" ,
104+ type = "image" ,
105+ sync = False ,
106+ ),
107+ OpenAiDefinition (
108+ module = "openai.resources.images" ,
109+ object = "Images" ,
110+ method = "edit" ,
111+ type = "image" ,
112+ sync = True ,
113+ ),
114+ OpenAiDefinition (
115+ module = "openai.resources.images" ,
116+ object = "AsyncImages" ,
117+ method = "edit" ,
118+ type = "image" ,
119+ sync = False ,
120+ ),
121+ OpenAiDefinition (
122+ module = "openai.resources.images" ,
123+ object = "Images" ,
124+ method = "create_variation" ,
125+ type = "image" ,
126+ sync = True ,
127+ ),
128+ OpenAiDefinition (
129+ module = "openai.resources.images" ,
130+ object = "AsyncImages" ,
131+ method = "create_variation" ,
132+ type = "image" ,
133+ sync = False ,
134+ ),
93135 OpenAiDefinition (
94136 module = "openai.resources.completions" ,
95137 object = "Completions" ,
@@ -354,9 +396,12 @@ def _extract_chat_response(kwargs: Any) -> Any:
354396
355397
356398def _get_langfuse_data_from_kwargs (resource : OpenAiDefinition , kwargs : Any ) -> Any :
357- default_name = (
358- "OpenAI-embedding" if resource .type == "embedding" else "OpenAI-generation"
359- )
399+ if resource .type == "embedding" :
400+ default_name = "OpenAI-embedding"
401+ elif resource .type == "image" :
402+ default_name = "OpenAI-image"
403+ else :
404+ default_name = "OpenAI-generation"
360405 name = kwargs .get ("name" , default_name )
361406
362407 if name is None :
@@ -417,6 +462,8 @@ def _get_langfuse_data_from_kwargs(resource: OpenAiDefinition, kwargs: Any) -> A
417462 prompt = _extract_chat_prompt (kwargs )
418463 elif resource .type == "embedding" :
419464 prompt = kwargs .get ("input" , None )
465+ elif resource .type == "image" :
466+ prompt = kwargs .get ("prompt" , None )
420467
421468 parsed_temperature = (
422469 kwargs .get ("temperature" , 1 )
@@ -479,6 +526,44 @@ def _get_langfuse_data_from_kwargs(resource: OpenAiDefinition, kwargs: Any) -> A
479526 modelParameters ["dimensions" ] = parsed_dimensions
480527 if parsed_encoding_format != "float" :
481528 modelParameters ["encoding_format" ] = parsed_encoding_format
529+ elif resource .type == "image" :
530+ # Image generation parameters
531+ modelParameters = {}
532+
533+ parsed_size = (
534+ kwargs .get ("size" , None )
535+ if not isinstance (kwargs .get ("size" , None ), NotGiven )
536+ else None
537+ )
538+ if parsed_size is not None :
539+ modelParameters ["size" ] = parsed_size
540+
541+ parsed_quality = (
542+ kwargs .get ("quality" , None )
543+ if not isinstance (kwargs .get ("quality" , None ), NotGiven )
544+ else None
545+ )
546+ if parsed_quality is not None :
547+ modelParameters ["quality" ] = parsed_quality
548+
549+ parsed_style = (
550+ kwargs .get ("style" , None )
551+ if not isinstance (kwargs .get ("style" , None ), NotGiven )
552+ else None
553+ )
554+ if parsed_style is not None :
555+ modelParameters ["style" ] = parsed_style
556+
557+ parsed_response_format = (
558+ kwargs .get ("response_format" , None )
559+ if not isinstance (kwargs .get ("response_format" , None ), NotGiven )
560+ else None
561+ )
562+ if parsed_response_format is not None :
563+ modelParameters ["response_format" ] = parsed_response_format
564+
565+ if parsed_n is not None and isinstance (parsed_n , int ) and parsed_n > 1 :
566+ modelParameters ["n" ] = parsed_n
482567 else :
483568 modelParameters = {
484569 "temperature" : parsed_temperature ,
@@ -791,6 +876,33 @@ def _get_langfuse_data_from_default_response(
791876 "count" : len (data ),
792877 }
793878
879+ elif resource .type == "image" :
880+ data = response .get ("data" , [])
881+ completion = []
882+ for item in data :
883+ image_data = item .__dict__ if hasattr (item , "__dict__" ) else item
884+ image_result = {}
885+
886+ # Handle URL response
887+ if image_data .get ("url" ):
888+ image_result ["url" ] = image_data ["url" ]
889+
890+ # Handle base64 response
891+ if image_data .get ("b64_json" ):
892+ # Wrap in LangfuseMedia for proper handling
893+ base64_data_uri = f"data:image/png;base64,{ image_data ['b64_json' ]} "
894+ image_result ["image" ] = LangfuseMedia (base64_data_uri = base64_data_uri )
895+
896+ # Include revised_prompt if present (DALL-E 3)
897+ if image_data .get ("revised_prompt" ):
898+ image_result ["revised_prompt" ] = image_data ["revised_prompt" ]
899+
900+ completion .append (image_result )
901+
902+ # If only one image, unwrap from list
903+ if len (completion ) == 1 :
904+ completion = completion [0 ]
905+
794906 usage = _parse_usage (response .get ("usage" , None ))
795907
796908 return (model , completion , usage )
@@ -842,6 +954,28 @@ def _wrap(
842954 try :
843955 openai_response = wrapped (** arg_extractor .get_openai_args ())
844956
957+ # Handle image generation (non-streaming)
958+ if open_ai_resource .type == "image" :
959+ model , completion , usage = _get_langfuse_data_from_default_response (
960+ open_ai_resource ,
961+ (openai_response and openai_response .__dict__ )
962+ if _is_openai_v1 ()
963+ else openai_response ,
964+ )
965+
966+ # Calculate image count for usage tracking
967+ image_count = 1
968+ if isinstance (completion , list ):
969+ image_count = len (completion )
970+
971+ generation .update (
972+ model = model ,
973+ output = completion ,
974+ usage_details = {"output" : image_count , "total" : image_count , "unit" : "IMAGES" },
975+ ).end ()
976+
977+ return openai_response
978+
845979 if _is_streaming_response (openai_response ):
846980 return LangfuseResponseGeneratorSync (
847981 resource = open_ai_resource ,
@@ -913,6 +1047,28 @@ async def _wrap_async(
9131047 try :
9141048 openai_response = await wrapped (** arg_extractor .get_openai_args ())
9151049
1050+ # Handle image generation (non-streaming)
1051+ if open_ai_resource .type == "image" :
1052+ model , completion , usage = _get_langfuse_data_from_default_response (
1053+ open_ai_resource ,
1054+ (openai_response and openai_response .__dict__ )
1055+ if _is_openai_v1 ()
1056+ else openai_response ,
1057+ )
1058+
1059+ # Calculate image count for usage tracking
1060+ image_count = 1
1061+ if isinstance (completion , list ):
1062+ image_count = len (completion )
1063+
1064+ generation .update (
1065+ model = model ,
1066+ output = completion ,
1067+ usage_details = {"output" : image_count , "total" : image_count , "unit" : "IMAGES" },
1068+ ).end ()
1069+
1070+ return openai_response
1071+
9161072 if _is_streaming_response (openai_response ):
9171073 return LangfuseResponseGeneratorAsync (
9181074 resource = open_ai_resource ,
0 commit comments