o
    @Tj1                     @   s   d Z ddlZddlZddlZddl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dZdZddhZd	ed
edefddZG dd deZdS )uH   OpenAI LLM provider — GPT-5.4 Vision + Responses API image_generation.    N)settings)BaseLLMProviderGarmentAnalysisStyleAnalysisStyleAnalysisTemp)GARMENT_ANALYSIS_PROMPTGHOST_MANNEQUIN_EDIT_PROMPTSTYLE_ANALYSIS_PROMPTSTYLE_ANALYSIS_TEMPVTON_PROMPTzhttps://api.openai.com/v1zgpt-5.4-minizgpt-5.4modeltemperaturereturnc                 C   s   | t v rd|iS i S )u  Sampling params for ``model`` — include ``temperature`` only if supported.

    Keeps a low, deterministic ``temperature`` for models that honor it while
    transparently dropping the param for models that don't, instead of
    hard-failing the request.
    r   )TEMPERATURE_CAPABLE_MODELS)r   r    r   7/home/ubuntu/htdocs/ootd-api/app/services/llm/openai.py_sampling_params1   s   r   c                   @   s   e Zd ZdZdddZdeeef fddZdeeef fdd	Zd
edefddZ	de
dee fddZ	dde
dedededB de
dB f
ddZ	dde
dee
 dee dedB 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 ) OpenAIProviderz3OpenAI API provider using httpx for async requests.r   Nc                 C   s    t j| _| jstd d S d S )Nu4   OPENAI_API_KEY is not set — OpenAI calls will fail)r   OPENAI_API_KEYapi_keyloggerwarningselfr   r   r   __init__@   s   zOpenAIProvider.__init__c                 C   s   dd| j  iS )NAuthorizationBearer r   r   r   r   r   _auth_headerE   s   zOpenAIProvider._auth_headerc                 C   s   d| j  ddS )Nr   zapplication/json)r   zContent-Typer   r   r   r   r   _json_headersH   s   
zOpenAIProvider._json_headerspayloadc              	      s   t  d}tjdd4 I dH 0}|j|||  dI dH }|js2td|d|j	|j
 |  | W  d  I dH  S 1 I dH sFw   Y  dS )ab  POST to /chat/completions, surfacing OpenAI's error body on failure.

        ``raise_for_status()`` alone discards the response body, but that body
        carries the actionable detail (unknown model, unsupported parameter
        value, etc.). Logging it mirrors what ``generate_ghost_mannequin``
        already does for the images endpoint.
        z/chat/completionsg      N@timeoutN)jsonheadersu9   OpenAI chat/completions failed (model=%s): HTTP %d — %sr   )OPENAI_BASE_URLhttpxAsyncClientpostr   
is_successr   errorgetstatus_codetextraise_for_statusr#   )r   r    urlclientrespr   r   r   _chat_completionN   s   
0zOpenAIProvider._chat_completionimagec                    s   t | }tddtddd| dddgd	gd
ttddddid}| |I dH }|d d d d }t|}t	|t
rW| D ]}t	|trV|} nqKt	|tsftdt| g S dd |D S )z/Analyze garments in image using GPT-5.4 Vision.userr-   typer-   	image_urldata:image/jpeg;base64,highr/   detailr6   r7   rolecontentr   messagesg?   r6   json_objectmax_completion_tokensresponse_formatNchoicesr   messager?   z"Unexpected GPT response format: %sc                 S   s   g | ]	}t d i |qS )r   )r   ).0itemr   r   r   
<listcomp>   s    z2OpenAIProvider.analyze_garment.<locals>.<listcomp>)base64	b64encodedecodeOPENAI_VISION_MODELr   r   r2   r#   loads
isinstancedictvalueslistr   r*   r6   )r   r3   	b64_imager    datar-   parsedvr   r   r   analyze_garmentc   s@   



zOpenAIProvider.analyze_garmentrJ   modeimage_modelc              	      s4  |pt j}tj|j|jp|jd}dd|dffg}||ddddd	}|d
v r+d|d< t d}	tjdd4 I dH /}
|
j	|	||| 
 dI dH }|js[|j}td|j| |  | }W d  I dH  n1 I dH sow   Y  |dpzg }|std dS |d dd}|std dS t|S )a  Generate ghost mannequin via /v1/images/edits.

        Skips the Responses-API agent reasoning overhead by calling the direct
        edits endpoint; ``input_fidelity=high`` preserves logos, stitching, and
        color patterns more faithfully than the previous setup. The ``mode``
        parameter is accepted for signature compatibility with Gemini but is
        currently ignored (single "edit" path). ``image_model`` selects the
        image-generation model; ``None`` falls back to ``OPENAI_IMAGE_MODEL``.
        )category_maincategory_subimage[]z
source.jpg
image/jpeg1	1024x1024lowpngr   promptnsizequalityoutput_format)zgpt-image-1zgpt-image-1.5r9   input_fidelity/images/editsg     f@r!   NrV   filesr$   u-   OpenAI ghost mannequin failed: HTTP %d — %srV   z%OpenAI images/edits returned no imager   b64_json z+OpenAI images/edits returned empty b64_json)r   OPENAI_IMAGE_MODELr   formatr\   r]   r%   r&   r'   r(   r   r)   r-   r   r*   r,   r.   r#   r+   r   rL   	b64decode)r   r3   rJ   rZ   r[   r   re   rm   rV   r/   r0   r1   
error_bodybodyitemsb64r   r   r   generate_ghost_mannequin   sV   



(


z'OpenAIProvider.generate_ghost_mannequinperson_imageghost_images
categoriesc              	      st  |pt j}ddd t|D }tjt||d}dd|dffg}t|D ]\}	}
|dd|	 d	|
d
ff q'||ddddd}t d}t	j
dd4 I dH /}|j||||  dI dH }|jss|j}td|j| |  | }W d  I dH  n1 I dH sw   Y  |dpg }|std dS |d dd}|rt|nd}|du rtd |dfS )u  Composite ghost garments onto a person via /v1/images/edits.

        Sends the person photo and each ghost image as multipart ``image[]``
        parts. The direct edits endpoint skips the Responses-API agent
        reasoning, so it is notably faster but does not return any free-text
        remark — the second tuple slot is always None. ``image_model`` selects
        the image-generation model; ``None`` falls back to ``OPENAI_IMAGE_MODEL``.
        z, c                 s   s(    | ]\}}d |d  d| V  qdS )#   =Nr   )rI   icatr   r   r   	<genexpr>   s   & z/OpenAIProvider.generate_vton.<locals>.<genexpr>)garment_countcatalogr^   z
person.jpgr_   garment_z.pngz	image/pngr`   	1024x1536rb   rc   rd   rk   g      n@r!   Nrl   u"   OpenAI VTON failed: HTTP %d — %srV   z'OpenAI VTON response contained no image)NNr   rn   ro   )r   rp   join	enumerater   rq   lenappendr%   r&   r'   r(   r   r)   r-   r   r*   r,   r.   r#   r+   r   rL   rr   )r   rx   ry   rz   r[   r   r   re   rm   r~   grV   r/   r0   r1   rs   rt   ru   rv   image_bytesr   r   r   generate_vton   sV   

	
(

zOpenAIProvider.generate_vtonc                       t | }tddtddd| dddgd	gd
ttddddid}| |I dH }|d d d d }t|}t	di |S )z2Analyze overall outfit style using GPT-5.4 Vision.r4   r-   r5   r7   r8   r9   r:   r<   r=   r@   333333?rB   r6   rC   rD   NrG   r   rH   r?   r   )
rL   rM   rN   rO   r	   r   r2   r#   rP   r   r   r3   rU   r    rV   r-   rW   r   r   r   analyze_style  .   
zOpenAIProvider.analyze_stylec                    r   )z7Context-aware outfit style analysis using GPT-5.4 Mini.r4   r-   r5   r7   r8   r9   r:   r<   r=   r@   r   rB   r6   rC   rD   NrG   r   rH   r?   r   )
rL   rM   rN   OPENAI_MINI_MODELr
   r   r2   r#   rP   r   r   r   r   r   analyze_style_temp7  r   z!OpenAIProvider.analyze_style_temp)r   N)N)__name__
__module____qualname____doc__r   rR   strr   r   r2   bytesrT   r   rY   rw   tupler   r   r   r   r   r   r   r   r   r   =   sB    
1
L
@!r   )r   rL   r#   loggingr&   
app.configr   app.services.llm.baser   r   r   r   app.services.llm.promptsr   r   r	   r
   r   	getLoggerr   r   r%   rO   OPENAI_VISION_MODEL_HIGHr   r   r   floatrR   r   r   r   r   r   r   <module>   s     
