11import asyncio
2+ import io
23import json
34import os
45import time
@@ -471,17 +472,12 @@ async def get_user_avatar(req):
471472 ctx .add_get ("/avatar/user" , get_user_avatar )
472473
473474 async def get_agent_avatar (req ):
474- role = req .match_info ["role" ]
475475 mode = req .query .get ("mode" , "light" )
476476
477477 # Cache for 1 hour # "Cache-Control": "public, max-age=3600",
478478 headers = {"Content-Type" : "image/svg+xml" }
479479
480480 candidate_paths = [
481- os .path .join (ctx .get_user_path (), role + "." + mode + ".png" ),
482- os .path .join (ctx .get_user_path (), role + "." + mode + ".svg" ),
483- os .path .join (ctx .get_user_path (), role + ".png" ),
484- os .path .join (ctx .get_user_path (), role + ".svg" ),
485481 os .path .join (ctx .get_user_path (), "agent." + mode + ".png" ),
486482 os .path .join (ctx .get_user_path (), "agent." + mode + ".svg" ),
487483 os .path .join (ctx .get_user_path (), "agent.png" ),
@@ -505,7 +501,118 @@ async def get_agent_avatar(req):
505501 """
506502 return web .Response (text = default_avatar , headers = headers )
507503
508- ctx .add_get ("/agents/avatar/{role}" , get_agent_avatar )
504+ ctx .add_get ("/agents/avatar" , get_agent_avatar )
505+
506+ async def upload_user_avatar (request ):
507+ user = ctx .get_username (request )
508+ user_path = ctx .get_user_path (user = user )
509+
510+ # Ensure the user directory exists
511+ os .makedirs (user_path , exist_ok = True )
512+
513+ # Parse multipart form data
514+ reader = await request .multipart ()
515+ field = await reader .next ()
516+
517+ if field is None or field .name != "file" :
518+ raise Exception ("No file provided" )
519+
520+ filename = field .filename or ""
521+ content_type = field .headers .get ("Content-Type" , "" ).lower ()
522+
523+ # Read file data
524+ file_data = await field .read ()
525+
526+ # Determine file type from extension or content type
527+ ext = os .path .splitext (filename )[1 ].lower () if filename else ""
528+
529+ if ext == ".svg" or content_type == "image/svg+xml" :
530+ # Save SVG directly
531+ avatar_path = os .path .join (user_path , "avatar.svg" )
532+ with open (avatar_path , "wb" ) as f :
533+ f .write (file_data )
534+ elif ext == ".png" or content_type == "image/png" :
535+ # Save PNG directly
536+ avatar_path = os .path .join (user_path , "avatar.png" )
537+ with open (avatar_path , "wb" ) as f :
538+ f .write (file_data )
539+ else :
540+ # Try to convert to PNG using Pillow
541+ try :
542+ from PIL import Image
543+
544+ img = Image .open (io .BytesIO (file_data ))
545+ # Convert to RGB if necessary (for formats like JPEG)
546+ if img .mode in ("RGBA" , "P" ):
547+ img = img .convert ("RGBA" )
548+ elif img .mode != "RGB" :
549+ img = img .convert ("RGB" )
550+
551+ avatar_path = os .path .join (user_path , "avatar.png" )
552+ img .save (avatar_path , "PNG" )
553+ except ImportError :
554+ raise Exception (
555+ "Only SVG and PNG formats are supported. Install Pillow to convert other image formats."
556+ ) from None
557+
558+ return web .json_response ({"success" : True , "path" : avatar_path })
559+
560+ ctx .add_post ("/user/avatar" , upload_user_avatar )
561+
562+ async def upload_agent_avatar (request ):
563+ user_path = ctx .get_user_path ()
564+
565+ # Ensure the user directory exists
566+ os .makedirs (user_path , exist_ok = True )
567+
568+ # Parse multipart form data
569+ reader = await request .multipart ()
570+ field = await reader .next ()
571+
572+ if field is None or field .name != "file" :
573+ raise Exception ("No file provided" )
574+
575+ filename = field .filename or ""
576+ content_type = field .headers .get ("Content-Type" , "" ).lower ()
577+
578+ # Read file data
579+ file_data = await field .read ()
580+
581+ # Determine file type from extension or content type
582+ ext = os .path .splitext (filename )[1 ].lower () if filename else ""
583+
584+ if ext == ".svg" or content_type == "image/svg+xml" :
585+ # Save SVG directly
586+ avatar_path = os .path .join (user_path , "agent.svg" )
587+ with open (avatar_path , "wb" ) as f :
588+ f .write (file_data )
589+ elif ext == ".png" or content_type == "image/png" :
590+ # Save PNG directly
591+ avatar_path = os .path .join (user_path , "agent.png" )
592+ with open (avatar_path , "wb" ) as f :
593+ f .write (file_data )
594+ else :
595+ # Try to convert to PNG using Pillow
596+ try :
597+ from PIL import Image
598+
599+ img = Image .open (io .BytesIO (file_data ))
600+ # Convert to RGB if necessary (for formats like JPEG)
601+ if img .mode in ("RGBA" , "P" ):
602+ img = img .convert ("RGBA" )
603+ elif img .mode != "RGB" :
604+ img = img .convert ("RGB" )
605+
606+ avatar_path = os .path .join (user_path , "agent.png" )
607+ img .save (avatar_path , "PNG" )
608+ except ImportError :
609+ raise Exception (
610+ "Only SVG and PNG formats are supported. Install Pillow to convert other image formats."
611+ ) from None
612+
613+ return web .json_response ({"success" : True , "path" : avatar_path })
614+
615+ ctx .add_post ("/agents/avatar" , upload_agent_avatar )
509616
510617 async def chat_request (openai_request , context ):
511618 chat = openai_request
0 commit comments