66# このソースコードは MITライセンス の下でライセンスされています。
77# ライセンスの詳細については、このプロジェクトのLICENSEファイルを参照してください。
88
9+ import copy
910import httpx
1011from datetime import datetime
1112from httpx import ReadTimeout
@@ -29,36 +30,53 @@ def send_onetime_message(self, text:str):
2930 def send_message (
3031 self ,
3132 text : str ,
33+ images : list [str ],
3234 listener : SendMessageListener ) -> str :
3335
3436 try :
35- return self .send_message_completions_streaming (text , listener )
37+ return self .send_message_completions_streaming (text , images , listener )
3638
3739 except StreamNotAllowedError :
3840 self .messages = self .messages [:- 1 ]
39- return self .send_message_completions_not_streaming (text , listener )
41+ return self .send_message_completions_not_streaming (text , images , listener )
4042
4143 except ResponsesApiRequiredError :
4244 self .messages = self .messages [:- 1 ]
4345 try :
44- return self .send_message_responses_streaming (text , listener )
46+ return self .send_message_responses_streaming (text , images , listener )
4547 except StreamNotAllowedError :
4648 self .messages = self .messages [:- 1 ]
47- return self .send_message_responses_not_streaming (text , listener )
49+ return self .send_message_responses_not_streaming (text , images , listener )
4850
4951 # メッセージを送信して回答を得る(Completions API・ストリーミング版)
5052 def send_message_completions_streaming (
5153 self ,
5254 text : str ,
55+ images : list [str ],
5356 listener : SendMessageListener ) -> str :
5457
5558 try :
5659 self .stop_send_event .clear ()
5760
5861 self .messages .append ({"role" : "user" , "content" : text })
59- messages = self .get_history ()
62+ messages = copy .deepcopy (self .get_history ())
63+
64+ if images and len (images ) > 0 :
65+ messages = messages [:- 1 ]
66+ content = []
67+ if text :
68+ content .append ({"type" : "text" , "text" : text })
69+
70+ for b64 in images :
71+ if not b64 .startswith ("data:" ):
72+ b64 = f"data:image/png;base64,{ b64 } "
73+ content .append ({"type" : "image_url" , "image_url" : {"url" : b64 }})
74+
75+ messages .append ({"role" : "user" , "content" : content })
76+
6077 if not self .model .startswith ("o1" ) and not self .model .startswith ("o3" ) and self .instruction :
6178 messages .insert (0 , {"role" : "system" , "content" : self .instruction })
79+
6280 stream = self .client .chat .completions .create (model = self .model , messages = messages , stream = True )
6381
6482 content = ""
@@ -135,14 +153,29 @@ def send_message_completions_streaming(
135153 def send_message_completions_not_streaming (
136154 self ,
137155 text : str ,
156+ images : list [str ],
138157 listener : SendMessageListener ) -> str :
139158
140159 try :
141160 no_exception = False
142161 listener .on_non_streaming_start ()
143162
144163 self .messages .append ({"role" : "user" , "content" : text })
145- messages = self .get_history ()
164+ messages = copy .deepcopy (self .get_history ())
165+
166+ if images and len (images ) > 0 :
167+ messages = messages [:- 1 ]
168+ content = []
169+ if text :
170+ content .append ({"type" : "text" , "text" : text })
171+
172+ for b64 in images :
173+ if not b64 .startswith ("data:" ):
174+ b64 = f"data:image/png;base64,{ b64 } "
175+ content .append ({"type" : "image_url" , "image_url" : {"url" : b64 }})
176+
177+ messages .append ({"role" : "user" , "content" : content })
178+
146179 if not self .model .startswith ("o1" ) and not self .model .startswith ("o3" ) and self .instruction :
147180 messages .insert (0 , {"role" : "system" , "content" : self .instruction })
148181
@@ -204,6 +237,7 @@ def send_message_completions_not_streaming(
204237 def send_message_responses_streaming (
205238 self ,
206239 text : str ,
240+ images : list [str ],
207241 listener : SendMessageListener ) -> str :
208242
209243 try :
@@ -232,6 +266,11 @@ def convert_message(m):
232266
233267 responses_input = [convert_message (m ) for m in messages ]
234268
269+ if images and len (images ) > 0 :
270+ last_message = responses_input [- 1 ]
271+ for image in images :
272+ last_message ["content" ].append ({"type" : "input_image" , "image_url" : image })
273+
235274 content = ""
236275 sentence = ""
237276 paragraph = ""
@@ -328,6 +367,7 @@ def convert_message(m):
328367 def send_message_responses_not_streaming (
329368 self ,
330369 text : str ,
370+ images : list [str ],
331371 listener : SendMessageListener ) -> str :
332372
333373 try :
@@ -339,9 +379,32 @@ def send_message_responses_not_streaming(
339379 if not self .model .startswith ("o1" ) and not self .model .startswith ("o3" ) and self .instruction :
340380 messages .insert (0 , {"role" : "system" , "content" : self .instruction })
341381
382+ def convert_message (m ):
383+ role = m ["role" ]
384+ if role == "assistant" :
385+ content_type = "output_text"
386+ else :
387+ content_type = "input_text"
388+ return {
389+ "role" : role ,
390+ "content" : [
391+ {
392+ "type" : content_type ,
393+ "text" : m ["content" ],
394+ }
395+ ],
396+ }
397+
398+ responses_input = [convert_message (m ) for m in messages ]
399+
400+ if images and len (images ) > 0 :
401+ last_message = responses_input [- 1 ]
402+ for image in images :
403+ last_message ["content" ].append ({"type" : "input_image" , "image_url" : image })
404+
342405 response = self .client .responses .create (
343406 model = self .model ,
344- input = messages ,
407+ input = responses_input ,
345408 timeout = httpx .Timeout (300.0 , connect = 5.0 )
346409 )
347410
0 commit comments