2828)
2929from src .utils .datetime_utils import utc_isoformat
3030from src .utils .logging_config import logger
31+ from src .utils .image_processor import process_uploaded_image
32+
33+
34+ # 图片上传响应模型
35+ class ImageUploadResponse (BaseModel ):
36+ success : bool
37+ image_content : str | None = None
38+ thumbnail_content : str | None = None
39+ width : int | None = None
40+ height : int | None = None
41+ format : str | None = None
42+ mime_type : str | None = None
43+ size_bytes : int | None = None
44+ error : str | None = None
45+
3146
3247chat = APIRouter (prefix = "/chat" , tags = ["chat" ])
3348
@@ -391,13 +406,18 @@ async def chat_agent(
391406 query : str = Body (...),
392407 config : dict = Body ({}),
393408 meta : dict = Body ({}),
409+ image_content : str | None = Body (None ),
394410 current_user : User = Depends (get_required_user ),
395411 db : Session = Depends (get_db ),
396412):
397413 """使用特定智能体进行对话(需要登录)"""
398414 start_time = asyncio .get_event_loop ().time ()
399415
400416 logger .info (f"agent_id: { agent_id } , query: { query } , config: { config } , meta: { meta } " )
417+ logger .info (f"image_content present: { image_content is not None } " )
418+ if image_content :
419+ logger .info (f"image_content length: { len (image_content )} " )
420+ logger .info (f"image_content preview: { image_content [:50 ]} ..." )
401421
402422 # 确保 request_id 存在
403423 if "request_id" not in meta or not meta .get ("request_id" ):
@@ -410,6 +430,7 @@ async def chat_agent(
410430 "server_model_name" : config .get ("model" , agent_id ),
411431 "thread_id" : config .get ("thread_id" ),
412432 "user_id" : current_user .id ,
433+ "has_image" : bool (image_content ),
413434 }
414435 )
415436
@@ -423,8 +444,32 @@ def make_chunk(content=None, **kwargs):
423444 )
424445
425446 async def stream_messages ():
426- # 代表服务端已经收到了请求
427- yield make_chunk (status = "init" , meta = meta , msg = HumanMessage (content = query ).model_dump ())
447+ # 构建多模态消息
448+ if image_content :
449+ # 多模态消息格式
450+ human_message = HumanMessage (
451+ content = [
452+ {"type" : "text" , "text" : query },
453+ {"type" : "image_url" , "image_url" : {"url" : f"data:image/jpeg;base64,{ image_content } " }},
454+ ]
455+ )
456+ message_type = "multimodal_image"
457+ else :
458+ # 普通文本消息
459+ human_message = HumanMessage (content = query )
460+ message_type = "text"
461+
462+ # 代表服务端已经收到了请求,发送前端友好的消息格式
463+ init_msg = {"role" : "user" , "content" : query , "type" : "human" }
464+
465+ # 如果有图片,添加图片相关信息
466+ if image_content :
467+ init_msg ["message_type" ] = "multimodal_image"
468+ init_msg ["image_content" ] = image_content
469+ else :
470+ init_msg ["message_type" ] = "text"
471+
472+ yield make_chunk (status = "init" , meta = meta , msg = init_msg )
428473
429474 # Input guard
430475 if conf .enable_content_guard and await content_guard .check (query ):
@@ -438,7 +483,7 @@ async def stream_messages():
438483 yield make_chunk (message = f"Error getting agent { agent_id } : { e } " , status = "error" )
439484 return
440485
441- messages = [{ "role" : "user" , "content" : query } ]
486+ messages = [human_message ]
442487
443488 # 构造运行时配置,如果没有thread_id则生成一个
444489 user_id = str (current_user .id )
@@ -458,8 +503,9 @@ async def stream_messages():
458503 thread_id = thread_id ,
459504 role = "user" ,
460505 content = query ,
461- message_type = "text" ,
462- extra_metadata = {"raw_message" : HumanMessage (content = query ).model_dump ()},
506+ message_type = message_type ,
507+ image_content = image_content ,
508+ extra_metadata = {"raw_message" : human_message .model_dump ()},
463509 )
464510 except Exception as e :
465511 logger .error (f"Error saving user message: { e } " )
@@ -543,6 +589,9 @@ async def stream_messages():
543589 except Exception as e :
544590 logger .error (f"Error streaming messages: { e } , { traceback .format_exc ()} " )
545591
592+ error_msg = f"Error streaming messages: { e } "
593+ error_type = "unexpected_error"
594+
546595 # 保存错误消息到数据库
547596 new_db = db_manager .get_session ()
548597 try :
@@ -551,13 +600,13 @@ async def stream_messages():
551600 new_conv_manager ,
552601 thread_id ,
553602 full_msg = full_msg ,
554- error_message = f"Error streaming messages: { e } " if not full_msg else None ,
555- error_type = "unexpected_error" ,
603+ error_message = error_msg if not full_msg else None ,
604+ error_type = error_type ,
556605 )
557606 finally :
558607 new_db .close ()
559608
560- yield make_chunk (message = f"Error streaming messages: { e } " , status = "error" )
609+ yield make_chunk (message = error_msg , status = "error" )
561610
562611 return StreamingResponse (stream_messages (), media_type = "application/json" )
563612
@@ -766,6 +815,8 @@ async def get_agent_history(
766815 "content" : msg .content ,
767816 "created_at" : msg .created_at .isoformat () if msg .created_at else None ,
768817 "error_type" : msg .extra_metadata .get ("error_type" ) if msg .extra_metadata else None ,
818+ "message_type" : msg .message_type , # 添加消息类型字段
819+ "image_content" : msg .image_content , # 添加图片内容字段
769820 }
770821
771822 # Add tool calls if present (for AI messages)
@@ -1143,3 +1194,47 @@ async def get_message_feedback(
11431194 except Exception as e :
11441195 logger .error (f"Error getting message feedback: { e } " )
11451196 raise HTTPException (status_code = 500 , detail = f"Failed to get feedback: { str (e )} " )
1197+
1198+
1199+ # =============================================================================
1200+ # > === 多模态图片支持分组 ===
1201+ # =============================================================================
1202+
1203+
1204+ @chat .post ("/image/upload" , response_model = ImageUploadResponse )
1205+ async def upload_image (file : UploadFile = File (...), current_user : User = Depends (get_required_user )):
1206+ """
1207+ 上传并处理图片,返回base64编码的图片数据
1208+ """
1209+ try :
1210+ # 验证文件类型
1211+ if not file .content_type or not file .content_type .startswith ("image/" ):
1212+ raise HTTPException (status_code = 400 , detail = "只支持图片文件上传" )
1213+
1214+ # 读取文件内容
1215+ image_data = await file .read ()
1216+
1217+ # 检查文件大小(10MB限制,超过后会压缩到5MB)
1218+ if len (image_data ) > 10 * 1024 * 1024 :
1219+ raise HTTPException (status_code = 400 , detail = "图片文件过大,请上传小于10MB的图片" )
1220+
1221+ # 处理图片
1222+ result = process_uploaded_image (image_data , file .filename )
1223+
1224+ if not result ["success" ]:
1225+ raise HTTPException (status_code = 400 , detail = f"图片处理失败: { result ['error' ]} " )
1226+
1227+ logger .info (
1228+ f"用户 { current_user .id } 成功上传图片: { file .filename } , "
1229+ f"尺寸: { result ['width' ]} x{ result ['height' ]} , "
1230+ f"格式: { result ['format' ]} , "
1231+ f"大小: { result ['size_bytes' ]} bytes"
1232+ )
1233+
1234+ return ImageUploadResponse (** result )
1235+
1236+ except HTTPException :
1237+ raise
1238+ except Exception as e :
1239+ logger .error (f"图片上传处理失败: { str (e )} , { traceback .format_exc ()} " )
1240+ raise HTTPException (status_code = 500 , detail = f"图片处理失败: { str (e )} " )
0 commit comments