o
    pif                     @   s   d Z ddlZddlZddlmZ ddlmZ ddlmZ ddl	m
Z
 ddlmZ ddlmZmZmZmZ dd	lmZmZmZmZmZ eeZd
ZdZG dd de
ZG dd deZdS )uY   Gemini LLM provider — google-genai SDK, Gemini 3.1 Flash (analysis + image generation).    N)Any)genai)types)	BaseModel)settings)BaseLLMProviderGarmentAnalysisStyleAnalysisStyleAnalysisTemp)GARMENT_ANALYSIS_PROMPTGHOST_MANNEQUIN_EDIT_PROMPTSTYLE_ANALYSIS_PROMPTSTYLE_ANALYSIS_TEMPVTON_PROMPTzgemini-2.5-flashzgemini-2.5-flash-imagec                   @   s   e Zd ZU dZee ed< dS )_GarmentAnalysisListz=Wrapper schema to enforce structured list output from Gemini.garmentsN)__name__
__module____qualname____doc__listr   __annotations__ r   r   7/home/ubuntu/ootd-poc-server/app/services/llm/gemini.pyr   "   s   
 r   c                   @   s   e Zd ZdZdddZ	ddededee dB de	fd	d
Z
dedee fddZdededededB fddZ	ddedee dee dedeedB edB f f
ddZdedefddZdedefddZdS )GeminiProviderz8Gemini API provider using the official google-genai SDK.returnNc                 C   s,   t j| _| jstd tj| jd| _d S )Nu4   GEMINI_API_KEY is not set — Gemini calls will fail)api_key)r   GEMINI_API_KEYr   loggerwarningr   Clientclient)selfr   r   r   __init__+   s   
zGeminiProvider.__init__promptimageschemac                    sV   t jddd}|r| |_| jjjjt|t j	j
|ddg|dI dH }t|jS )z6Send a vision analysis request and return parsed JSON.i   zapplication/json)max_output_tokensresponse_mime_type
image/jpegdata	mime_typemodelcontentsconfigN)r   GenerateContentConfigmodel_json_schemaresponse_json_schemar!   aiomodelsgenerate_contentGEMINI_ANALYSIS_MODELPart
from_bytesjsonloadstext)r"   r$   r%   r&   r0   responser   r   r   _analyze1   s   

zGeminiProvider._analyzec                    sh   | j t|tdI dH }t|trd|v rdd |d D S t|tr*dd |D S tdt| g S )z.Analyze garments in image using Gemini Vision.r&   Nr   c                 S      g | ]	}t d i |qS r   r   .0itemr   r   r   
<listcomp>N       z2GeminiProvider.analyze_garment.<locals>.<listcomp>c                 S   r@   rA   rB   rC   r   r   r   rF   R   rG   z%Unexpected Gemini response format: %s)	r>   r   r   
isinstancedictr   r   errortyper"   r%   parsedr   r   r   analyze_garmentF   s   
zGeminiProvider.analyze_garmentrE   modec                    s   zSt j|j|jp|jd}| jjjjt|t	j
j|ddgt	jddgddI dH }|js5td	 W dS |jd
 jjD ]}|jrL|jjrL|jj  W S q<td W dS  tyc   td Y dS w )z7Generate ghost mannequin using Gemini image generation.)category_maincategory_subr)   r*   IMAGETEXTresponse_modalitiesr-   Nz(Gemini image edit returned no candidatesr   z2Gemini image edit response contained no image dataz*Ghost mannequin generation failed (gemini))r   formatrP   rQ   r!   r4   r5   r6   GEMINI_IMAGE_MODELr   r8   r9   r1   
candidatesr   r   contentpartsinline_datar+   	Exception	exception)r"   r%   rE   rO   r$   r=   partr   r   r   generate_ghost_mannequinW   s8   




z'GeminiProvider.generate_ghost_mannequinFperson_imageghost_images
categoriesperson_has_alphac                    sJ  d dd t|D }tjt||d}|rdnd}|tjj||dg}|D ]}	|tjj|	dd q(z| j	j
jjt|tjdd	gd
dI dH }
W n ty[   td Y dS w |
jsftd dS d}g }|
jd jjD ]}|jr|jjr|jj}qqt|ddr||j qqd dd |D pd}|du rtd ||fS )zIComposite ghost garments onto a person via Gemini multi-image generation.z, c                 s   s(    | ]\}}d |d  d| V  qdS )#   =Nr   )rD   icatr   r   r   	<genexpr>   s    
z/GeminiProvider.generate_vton.<locals>.<genexpr>)garment_countcatalogz	image/pngr)   r*   rR   rS   rT   r-   NzVTON generation failed (gemini))NNz"Gemini VTON returned no candidatesr   r<    c                 s   s     | ]}|  r|  V  qd S N)strip)rD   chunkr   r   r   ri      s    z,Gemini VTON response contained no image data)join	enumerater   rV   lenr   r8   r9   appendr!   r4   r5   r6   rW   r1   r\   r   r]   rX   r   rY   rZ   r[   r+   getattrr<   )r"   r`   ra   rb   rc   rk   r$   person_mimer/   gr=   image_bytesnote_chunksr^   noter   r   r   generate_vton~   sN   





zGeminiProvider.generate_vtonc                    &   | j t|tdI dH }tdi |S )z1Analyze overall outfit style using Gemini Vision.r?   Nr   )r>   r   r	   rL   r   r   r   analyze_style   
   zGeminiProvider.analyze_stylec                    r{   )z8Context-aware outfit style analysis using Gemini Vision.r?   Nr   )r>   r   r
   rL   r   r   r   analyze_style_temp   r}   z!GeminiProvider.analyze_style_temp)r   Nrm   )F)r   r   r   r   r#   strbytesrK   r   r   r>   r   r   rN   r_   booltuplerz   r	   r|   r
   r~   r   r   r   r   r   (   sJ    



,
2r   )r   r:   loggingtypingr   googler   google.genair   pydanticr   
app.configr   app.services.llm.baser   r   r	   r
   app.services.llm.promptsr   r   r   r   r   	getLoggerr   r   r7   rW   r   r   r   r   r   r   <module>   s    
