o
    QFjO                     @  s  d dl m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	 d dl
mZmZ d dlmZmZ d dlmZmZ d dlmZmZ d d	lmZ G d
d deZG dd deZeG dd dZdrddZ	dsdddtddZG dd dZdud"d#Zdvd(d)Z dwd+d,Z!dxd1d2Z"G d3d4 d4Z#dyd6d7Z$G d8d9 d9Z%dyd:d;Z&dzd=d>Z'd{d@dAZ(d|dDdEZ)d}dHdIZ*dJZ+dKZ,dLZ-d~dPdQZ.d~dRdSZ/ddTdUZ0ddVdWZ1dd\d]Z2dd^d_Z3ddadbZ4drdcddZ5ddhdiZ6ddkdlZ7ddmdnZ8ddpdqZ9dS )    )annotationsN)	dataclass)Path)AnyCallable)urlsplit
urlunsplit)	HTTPErrorURLError)Requesturlopen)DEFAULT_FRAMES_PER_ACTIONc                   @     e Zd ZdS )QwenConfigErrorN__name__
__module____qualname__ r   r   7/opt/sixxie/releases/current/services/ai/qwen_client.pyr          r   c                   @  r   )QwenRequestErrorNr   r   r   r   r   r      r   r   c                   @  s>   e Zd ZU ded< ded< ded< edddZddd	Zd
S )
QwenConfigstrapi_keybase_urlmodelreturnc                 C  s
   t | jS N)mask_secretr   selfr   r   r   masked_api_key   s   
zQwenConfig.masked_api_keyc                 C  s   d| j d| jd| jdS )NzQwenConfig(api_key=z, base_url=z, model=))r"   r   r   r    r   r   r   __repr__#   s   zQwenConfig.__repr__N)r   r   )r   r   r   __annotations__propertyr"   r$   r   r   r   r   r      s   
 r   valuer   r   c                 C  s.   t | dk rdS | d d  d| dd   S )N   z***   z...)len)r'   r   r   r   r   ,   s   r   
.env.local
model_keysenv_path
str | Pathr.   tuple[str, ...] | Nonec                  s   t t| }i |dd tj D  |pd} dd } dd d}t fdd	|D d}|r=|r=|sHt	d
d
| d t|||dS )Nc                 S      i | ]\}}| d r||qS QWEN_
startswith.0keyr'   r   r   r   
<dictcomp>8        z$load_qwen_config.<locals>.<dictcomp>)QWEN_MODEL_REVIEWQWEN_MODEL_VISIONQWEN_MODEL_DEFAULTQWEN_API_KEY QWEN_BASE_URL/c                 3  s0    | ]}  |d  r  |d  V  qdS )r@   N)getstrip)r8   r9   mergedr   r   	<genexpr>=   s   . z#load_qwen_config.<locals>.<genexpr>zTQwen config is incomplete. Set QWEN_API_KEY, QWEN_BASE_URL, and one model key from: , .r   r   r   )_read_env_filer   osenvironitemsrC   rD   rstripnextr   joinr   )r/   r.   
env_valuespreferred_model_keysr   r   r   r   rE   r   load_qwen_config2   s   rT   c                   @  sT   e Zd Zedd#d
dZed$d%ddZd&ddZd'ddZd(ddZ	d)d d!Z
d"S )*QwenReviewClientopenerr   r   r   r   rW   Callable[..., Any]r   Nonec                C  "   || _ |d| _|| _|| _d S NrB   r   rO   r   r   rW   r!   r   r   r   rW   r   r   r   __init__I      
zQwenReviewClient.__init__r,   r/   r0   'QwenReviewClient'c                 C      t |dd}| |j|j|jdS )N)r<   r>   r=   r-   rJ   rT   r   r   r   clsr/   configr   r   r   from_envV   
   zQwenReviewClient.from_envpet_namedescriptionimage_bytesbytes
image_mimedict[str, Any]c          
   	   C  s   t ||}| jdddddd| d| dd	d
d|idgdgdd}| |}|di gd di dd}t|}	dd| j|d|	dS )Nsystema  You review a memorial desktop pet asset. Return compact JSON only. Assess high-fidelity likeness to the authorized pet reference, recognizable pet traits, calm low-disturbance desktop use, safe preview framing, and whether the asset avoids pixelization, blocky downsampling, therapy, revival, chat, or productivity positioning. The website shell may use pixel styling, but generated desktop pet assets must not be pixelized.rolecontentusertextz3Review this generated pet asset. Pet display name: z. Package description: z. The generated desktop pet asset must preserve high-fidelity likeness and must not be pixelized. Return JSON with keys score (0-1), passed (boolean), notes (short), risks (array).typers   	image_urlurlru   rv   r   r   messagestemperaturechoicesmessagerq   r@   okqwenidstatusproviderr   response_idreview)_to_data_urlr   _post_chat_completionsrC   _parse_review_content)
r!   rh   ri   rj   rl   data_urlpayloadrawrq   parsed_reviewr   r   r   review_pet_package^   s8   


 "z#QwenReviewClient.review_pet_packageactionc                C  s   t ||}t|}| jdddddd| d| dt d	| d
t dt dddd|idgdgdd}| |}|di gd di dd}	t|	}
dd| j|d|
dS )Nrn   az  You review one generated action row for a memorial desktop pet. Return compact JSON only. Check same pet identity, recognizable pet traits, full-body visibility, clean background removal readiness, and action semantics. Reject if the row changes species, becomes a simplified unrelated animal, contains cropped or broken bodies, or does not visibly perform the requested action.ro   rr   rs   z:Please review one generated action row. Pet display name: z. Action to verify: z. The zZ frames must look like the same pet identity and must visibly match the action semantics. z Return JSON with keys score (0-1), passed (boolean), action_ok (boolean), identity_ok (boolean), notes (short), risks (array). For walk, also return front_paw_sequence (z$ short strings), hind_paw_sequence (zw short strings), gait_cycle_ok (boolean), two_frame_loop_ok (boolean), and bad_frames (array of 1-based frame numbers).rt   rv   rw   rx   r   ry   r|   r}   rq   r@   r~   r   r   r   )r   "_action_visual_review_requirementsr   r   r   rC   r   )r!   rh   r   rj   rl   r   action_requirementsr   r   rq   r   r   r   r   review_action_frames   sJ   


#"z%QwenReviewClient.review_action_framesc          	      C  s   t ||}| jdddddd| dt dt d	t d
	ddd|idgdgdd}| |}|di gd di dd}t|}dd| j|d|dS )Nrn   a*  You review the final 6-row contact sheet for a memorial desktop pet. Return compact JSON only. Reject packages with wrong-facing frames or action rows that do not match hard required labels. Tail_wag may be a subtle low-disturbance row; do not reject solely because tail_wag resembles idle or look.ro   rr   rs   zSPlease review the final 6-row contact sheet before installation. Pet display name: zo. Rows are fixed: Row 1 idle, Row 2 sleep, Row 3 walk, Row 4 look, Row 5 sit, Row 6 tail_wag. Row 2 sleep: all z frames must keep the same sleeping direction/orientation; reject any flipped, opposite-facing, upright, or non-sleeping frame. Row 3 walk: this project intentionally uses a two-frame walk loop. All z frames must face and walk right in side view; reject any left-facing, front-facing, sitting, or loafing frame. Row 3 frames 1 and 2 must be two distinct walking key poses; Row 3 frames 3 through a{   intentionally repeat Row 3 frames 1 and 2 for looping. Reject if frames 1 and 2 are the same paw stance or only body translation. Row 4 look should use a stable body with head/eye direction changes. Row 6 tail_wag may be subtle; a small visible tail or rear-body variation is acceptable for a calm desktop pet. Do not reject solely because tail_wag resembles idle or look when identity, full-body visibility, walk direction, and sleep direction are acceptable. Also check same pet identity, full-body visibility, calm low-disturbance tone, and no text/scenery. Return JSON with keys score (0-1), passed (boolean), identity_ok (boolean), row_semantics_ok (boolean), direction_ok (boolean), row_distinct_ok (boolean), gait_cycle_ok (boolean for Row 3 walk), two_frame_loop_ok (boolean for Row 3 walk), repair_actions (array of action names needing regeneration), notes (short), risks (array).rt   rv   rw   rx   r   ry   r|   r}   rq   r@   r~   r   r   r   )r   r   r   r   rC   r   )	r!   rh   rj   rl   r   r   r   rq   r   r   r   r   review_contact_sheet   s@   
		*
."z%QwenReviewClient.review_contact_sheetr   c              
   C     t |d}tt| j|dd| j ddd}z%| j|dd}t |	 
dW  d    W S 1 s8w   Y  W d S  tyT } z	td	|j |d }~w tyd } ztd
|d }~w t jyu } ztd|d }~ww Nutf-8POSTBearer application/jsonAuthorizationzContent-Typedatamethodheaders<   timeoutzQwen request failed with HTTP z/Qwen request failed before receiving a responsez Qwen response was not valid JSONjsondumpsencoder   _chat_completions_urlr   r   rW   loadsreaddecoder	   r   coder
   JSONDecodeErrorr!   r   bodyrequestresponseexcr   r   r   r     .   
	(

z'QwenReviewClient._post_chat_completionsN
r   r   r   r   r   r   rW   rX   r   rY   r,   )r/   r0   r   r`   )
rh   r   ri   r   rj   rk   rl   r   r   rm   )
rh   r   r   r   rj   rk   rl   r   r   rm   )rh   r   rj   rk   rl   r   r   rm   r   rm   r   rm   )r   r   r   r   r^   classmethodrf   r   r   r   r   r   r   r   r   rU   H   s    

5
8ArU   pathr   dict[str, str]c                 C  sv   |   si S i }| jdd D ](}| }|r!|ds!d|vr"q|dd\}}| dd|| < q|S )Nr   )encoding#=   "')exists	read_text
splitlinesrD   r6   split)r   valuesraw_lineliner9   r'   r   r   r   rK   "  s   rK   r   r9   defaultintc              	   C  6   zt t| || W S  ttfy   | Y S w r   )r   r   rC   rD   	TypeError
ValueErrorr   r9   r   r   r   r   _env_int/  
   r   floatc              	   C  r   r   )r   r   rC   rD   r   r   r   r   r   r   
_env_float6  r   r   r   	Exceptionattemptretry_base_secondsc                 C  s   d }t | dd }|d urz|d}W n ty   d }Y nw |d ur<ztdttt| dW S  ty;   Y nw tt|d|d  dS )Nr   zRetry-After        g     r@r   )	getattrrC   AttributeErrormaxminr   r   rD   r   )r   r   r   retry_afterr   r   r   r   _retry_delay_seconds=  s   r   c                   @  s@   e Zd Zeddd
dZeddddZd ddZd!ddZdS )"QwenPhotoAnalyzerrV   r   r   r   r   rW   rX   r   rY   c                C  rZ   r[   r\   r]   r   r   r   r^   N  r_   zQwenPhotoAnalyzer.__init__r,   r/   r0   'QwenPhotoAnalyzer'c                 C  ra   )N)r=   r>   r<   r-   rJ   rb   rc   r   r   r   rf   [  rg   zQwenPhotoAnalyzer.from_envrh   notesimageslist[dict[str, Any]]rm   c          
   	   C  s   dd| d|d d  dg}|D ]}| ddt|d |d	 id
 q| jdddd|dgdd}| |}|di gd di dd}t|}	t|	S )Nrs   aq  Analyze these authorized pet reference images for a memorial desktop pet generator. Return compact JSON only with keys: species, base_color, accent_color, eye_color, body_shape, distinctive_marks, confidence, safe_for_generation, user_visible_summary. Do not include raw image details about people, home, location, file paths, or private memory text. Pet display name: z4. Optional owner notes for visual/action cues only:    rt   rv   rw   rk   mimerx   rn   aR  You extract safe visual traits for a high-fidelity, low-disturbance memorial desktop pet asset. Preserve natural proportions, coat texture, markings, eye color, and overall body type; do not recommend pixel art, pixelization, or blocky downsampling. Never frame the pet as revived, conscious, therapeutic, chatty, or productivity-related.ro   rr   r   ry   r|   r}   rq   r@   )appendr   r   r   rC   r   _normalize_photo_analysis)
r!   rh   r   r   rq   imager   r   content_textparsedr   r   r   analyze_pet_photosc  s4   


"z$QwenPhotoAnalyzer.analyze_pet_photosr   c              
   C  r   r   r   r   r   r   r   r     r   z(QwenPhotoAnalyzer._post_chat_completionsNr   r   )r/   r0   r   r   )rh   r   r   r   r   r   r   rm   r   )	r   r   r   r   r^   r   rf   r   r   r   r   r   r   r   M  s    
+r   r   c                 C  s"   |  d}|dr|S | dS )NrB   z/chat/completions)rO   endswith)r   cleanr   r   r   r     s   


r   c                
   @  s   e Zd ZdZdeejddddddd		dTddZe	 dUed!dVd%d&Z	d'd(dWd0d1Z
d'd(dWd2d3Zd'd(dXd7d8Zd9d(dYd;d<Zd=dd'd>dZdCdDZd=dd'dEd[dFdGZdddHd\dLdMZd]dOdPZd^dRdSZdS )_QwenImageGenerationClientr   Nr)         >@r        f@   r   )	
edit_modelrW   sleepmax_retriesr   request_delay_secondstimeout_secondscanonical_reference_limitaction_reference_limitr   r   r   r   r   
str | NonerW   rX   r   Callable[[float], Any]r   r   r   r   r   r   r   r   r   rY   c                C  s   || _ |d| _|| _|pd| _|| _|| _tdt|| _	tdt
|| _tdt
|	| _tdt
|
| _tdtt|d| _tdtt|d| _d S )NrB   qwen-image-editr   r   g      $@r   r   )r   rO   r   r   r   rW   r   r   r   r   r   r   r   r   r   r   r   )r!   r   r   r   r   rW   r   r   r   r   r   r   r   r   r   r   r^     s   
z"QwenImageGenerationClient.__init__r,   rV   r/   r0   'QwenImageGenerationClient'c                C  s   t t|}i |dd tj D }t|dd}|dp d}| |j|j|j	||t
|ddt|d	d
t|ddt|ddt
|ddt
|dddS )Nc                 S  r2   r3   r5   r7   r   r   r   r:     r;   z6QwenImageGenerationClient.from_env.<locals>.<dictcomp>)QWEN_MODEL_IMAGEr>   r-   QWEN_MODEL_IMAGE_EDITr   QWEN_IMAGE_MAX_RETRIESr)   QWEN_IMAGE_RETRY_BASE_SECONDSr    QWEN_IMAGE_REQUEST_DELAY_SECONDSr   QWEN_IMAGE_TIMEOUT_SECONDSr   $QWEN_IMAGE_CANONICAL_REFERENCE_LIMITr   !QWEN_IMAGE_ACTION_REFERENCE_LIMITr   )r   r   r   r   rW   r   r   r   r   r   r   )rK   r   rL   rM   rN   rT   rC   r   r   r   r   r   )rd   r/   rW   rR   rF   re   r   r   r   r   rf     s(   





z"QwenImageGenerationClient.from_envz	1024*1024)sizerh   r   analysisrm   r   r   r   c             
   C  s   |st dt| j t|||d}dd |d | j D }|d|i | jdd|dgid	|d
d
ddd}| |}	| jrF| | j t	|	}
| 
|
}dd| j|	d|d|	di t|dS )N8at least one image is required for Qwen image generationrh   r   r   c                 S  "   g | ]}d t |d |d iqS r   rk   r   r   r8   r   r   r   r   
<listcomp>     " zFQwenImageGenerationClient.generate_pet_spritesheet.<locals>.<listcomp>rs   rz   rr   ro   r   Fzpixel art, blocky pixels, 8-bit, text, logo, speech bubble, human, room background, medical claim, therapy claim, resurrection, chatbot, productivity assistant, transparent background, alpha transparency, checkerboard transparency, black cutout holesnr   	watermarkprompt_extendnegative_promptr   input
parametersr~   r   
request_id	image/pngusager   r   r   r  rj   rl   r  prompt_sha256)r   _ensure_qwen_image_modelr   _build_pet_image_promptr   r   _post_multimodal_generationr   r   _extract_generated_image_url_download_generated_imagerC   _sha256_text)r!   rh   r   r   r   r   promptrq   r   r   rv   rj   r   r   r   generate_pet_spritesheet  sD   	
	


z2QwenImageGenerationClient.generate_pet_spritesheetc                C  sZ   |st dt| j t|||d}dd |d | j D }|d|i | j|||dS )Nr  r  c                 S  r  r  r  r  r   r   r   r  *  r  zDQwenImageGenerationClient.generate_canonical_pet.<locals>.<listcomp>rs   )rq   r  r   )r   r  r   _build_canonical_pet_promptr   r   _generate_image)r!   rh   r   r   r   r   r  rq   r   r   r   generate_canonical_pet  s   	
z0QwenImageGenerationClient.generate_canonical_petr   canonical_image_bytesrk   c          
      C  sX   |st dt| j t||||d}dt|dig}	|	d|i | j|	||| jdS )N1canonical image is required for action generationr   rh   r   r   r   r  rs   rq   r  r   r   )r   r  r   _build_action_frames_promptr   r   r  )
r!   r   rh   r   r   r   r!  r   r  rq   r   r   r   generate_action_frames.  s   
z0QwenImageGenerationClient.generate_action_framesz1280*720layout_guide_image_bytesc                C  sx   |st d|st dt| j t||||d}	dt|dig}
|
dt|di |
d|	i | j|
|	|| jdS )Nr"  z:layout guide image is required for action strip generationr#  r   r  rs   r$  )r   r  r   _build_action_strip_promptr   r   r  )r!   r   rh   r   r   r   r!  r'  r   r  rq   r   r   r   generate_action_stripA  s   
z/QwenImageGenerationClient.generate_action_stripr   )candidate_indexprevious_frame_image_bytesr   frame_indexr*  r+  bytes | Nonec       
      	   C  s   |st d|dk s|tkrt dtd  t| j t|	o#|dk}t|||||||d}g }|dt|di |rG|dt|	di |d	|i | j|||
| jd
S )Nr"  r   "frame_index must be between 0 and r   walk)r   r,  r*  rh   r   r   has_previous_framer   r  rs   r$  )	r   r   r  r   bool_build_action_frame_promptr   r   r  )r!   r   r,  r*  rh   r   r   r   r!  r+  r   attach_previous_framer  rq   r   r   r   generate_action_frameX  s*   
	z/QwenImageGenerationClient.generate_action_frame)r*  r!  r   c                C  s   |dk s|t krtdt d  t| j t||||||d}	g }
|r.|
dt|di |
d|	i | j|
|	|| jdd	S )
Nr   r.  r   )r   r,  r*  rh   r   r   r   r  rs   zpixel art, text, logo, human, scenery, speech bubble, multiple copies, cropped body, transparent background, alpha transparency, checkerboard transparency, black cutout holes)rq   r  r   r   r  )r   r   r  r   "_build_minimal_action_frame_promptr   r   r  )r!   r   r,  r*  rh   r   r   r!  r   r  rq   r   r   r   generate_action_frame_minimal|  s,   
z7QwenImageGenerationClient.generate_action_frame_minimal)r   r  rq   r  r  c          
   
   C  s   |d u rd}|p
| j }|dd|dgid|dd|dd}| |}| jr+| | j t|}| |}	d	d
||d|	d|di t|dS )Na[  pixel art, blocky pixels, 8-bit, text, logo, speech bubble, human, room background, medical claim, therapy claim, resurrection, chatbot, productivity assistant, duplicate identity drift, transparent background, alpha transparency, checkerboard transparency, transparent holes inside body, black cutout holes, missing fur patches, broken alpha maskrz   rr   ro   r   Fr	  r  r~   r   r  r  r  r  )r   r  r   r   r  r  rC   r  )
r!   rq   r  r   r   r  r   r   rv   rj   r   r   r   r    s6   	



z)QwenImageGenerationClient._generate_imager   c                 C  st  t |d}tt| j|dd| j ddd}t| jd D ]}z'| j	|| j
d}t | dW  d    W   S 1 sDw   Y  W q! ty } z)|jd	krl|| jk rl| t||| j W Y d }~q!t|}td
|j | |d }~w ty } z|| jk r| t||| j W Y d }~q!td|d }~w t jy } ztd|d }~ww td)Nr   r   r   r   r   r   r   r   i  z'Qwen image generation failed with HTTP z8Qwen image generation failed before receiving a responsez1Qwen image generation response was not valid JSONz*Qwen image generation failed after retries)r   r   r   r   _multimodal_generation_urlr   r   ranger   rW   r   r   r   r   r	   r   r   r   r   _http_error_detailr   r
   r   )r!   r   r   r   r   r   r   detailr   r   r   r    s@   
	*


z5QwenImageGenerationClient._post_multimodal_generationrv   c              
   C  s   z| j || jd}| }W d    n1 sw   Y  W n% ty3 } z	td|j |d }~w tyC } ztd|d }~ww |sJtd|S )Nr   z/Qwen generated image download failed with HTTP z@Qwen generated image download failed before receiving a responsez'Qwen generated image download was empty)rW   r   r   r	   r   r   r
   )r!   rv   r   rj   r   r   r   r   r    s   

z3QwenImageGenerationClient._download_generated_image)r   r   r   r   r   r   r   r   rW   rX   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rY   r   )r/   r0   rW   rX   r   r   )rh   r   r   r   r   rm   r   r   r   r   r   rm   )r   r   rh   r   r   r   r   rm   r   r   r!  rk   r   r   r   rm   )r   r   rh   r   r   r   r   rm   r   r   r!  rk   r'  rk   r   r   r   rm   )r   r   r,  r   r*  r   rh   r   r   r   r   rm   r   r   r!  rk   r+  r-  r   r   r   rm   )r   r   r,  r   r*  r   rh   r   r   r   r   rm   r!  r-  r   r   r   rm   )rq   r   r  r   r   r   r   r   r  r   r   rm   r   )rv   r   r   rk   )r   r   r   r   r   timer   r^   r   rf   r  r   r&  r)  r4  r6  r  r  r  r   r   r   r   r     sL    "=),
,r   c                 C  sR   |  d}d}||r|S t|}|jr#|jr#t|j|j|ddfS | | S )NrB   z6/api/v1/services/aigc/multimodal-generation/generationr@   )rO   r   r   schemenetlocr   )r   r   target_pathpartsr   r   r   r7    s   

r7  r   c                 C  sH   dt  ddt  dt  dt  ddt  dd	t  d
dd}|| dS )NzHard rules for sleep: all z frames must be low lying, curled, or clearly sleeping; reject upright sitting, standing, walking, or any flipped opposite-facing sleep direction.zPHard rules for walk: this project intentionally uses a two-frame walk loop. All z{ frames must face and walk right in side view; frames 1 and 2 must be two distinct walking key poses, and frames 3 through a   intentionally repeat frames 1 and 2. reject any left-facing, front-facing, sitting, or loafing frame; reject if frames 1 and 2 are the same paw stance or only body translation. Set two_frame_loop_ok=true when frames 1 and 2 form a readable two-pose walk cycle and frames 3 through zk correctly copy them. Set gait_cycle_ok=true when the two-frame loop is acceptable for a small desktop pet.zAHard rules for look: keep one stable body orientation across all zp frames; only the head or eyes may move gently; reject full-body turns or body-direction changes within the row.zHard rules for sit: all zv frames must remain seated with rear and hind legs on the ground; reject standing, walking, or four-paw support poses.zHard rules for tail_wag: tail motion may be subtle, but the tail or rear-body variation must be visible; do not reject solely because the row is calm or low-disturbance.r   r/  looksittail_wagzMHard rules: preserve the same pet identity and reject wrong action semantics.r   rC   r   requirementsr   r   r   r     s   

	
r   r	   c                 C  s   z|   }W n ty   d}Y nw |sdS z	|jddd}W n ty,   t|}Y nw d| }|s8dS d|d d  S )	N    r@   r   replace)errors z: i,  )r   r   r   reprrQ   r   )r   r   rs   r   r   r   r9  #  s    r9  r   rm   c              	   C  s  |  ds
|  dr(tdt|  ddd d  dt|  ddd d  |  d	i }| d
g }|D ]2}| di  dg }t|trJ|g}t|trh|D ]}t|trg| drgt|d     S qQq6| dg }t|tr|D ]}t|tr| drt|d   S qvtd)Nr   r}   z)Qwen image generation returned an error: UnknownErrorP   z - r@      outputr|   rq   r   resultsrw   z;Qwen image generation response did not include an image URL)rC   r   r   
isinstancedictlist)r   rO  r|   choicerq   itemrP  r   r   r   r  4  s4   


r  r   rY   c                 C  s   |  dstd| dd S )Nz
qwen-imagezQWEN_MODEL_IMAGE must be a Qwen image generation/edit model such as qwen-image-2.0-pro, qwen-image-2.0, qwen-image-max, or qwen-image-plus; got rI   )r6   r   )r   r   r   r   r  L  s   
r  a  Use a flat pure magenta #FF00FF chroma-key background, not transparency. Do not generate transparent pixels anywhere in the output. The pet must have a fully opaque pet body with no transparent pixels, black holes, cutout holes, missing fur patches, or broken alpha mask. Keep #FF00FF and similar magenta colors completely out of the pet, fur, eyes, markings, highlights, shadows, and effects. u  使用纯蓝 #0000FF 单色背景，不要白底，不要透明背景，不要棋盘格，不要场景、地面、阴影、文字、编号、边框或分割线。蓝色背景必须铺满整张图片直到四周边缘，不要画成蓝色卡片、蓝色方块或局部背景板。角色不要站在地面上，不要接触阴影、落地阴影、投影、彩色反光或暗色渐变。蓝色背景只用于后续本地抠图，宠物身体、毛发、眼睛、花纹和边缘不要被背景颜色污染。af  Use a plain white clean background, not transparency and not chroma-key color. Do not generate transparent pixels anywhere in the output. Avoid floor shadows, colored halos, glow, scenery, checkerboard, text, labels, borders, or props. The white background is only for later local background removal; keep the pet body, fur, eyes, markings, and edges clean. rh   r   r   c                 C  s   | dg }t|tst|g}ddd |d d D pd}d|  d d }d	t d
| d d  d| dd d| dd d| dd d| dd d| dd d| d| dS )Ndistinctive_marksrH   c                 s  s    | ]}t |V  qd S r   r   r8   markr   r   r   rG   o  s    z*_build_pet_image_prompt.<locals>.<genexpr>   zauthorized reference markingsrJ     a1  Create a high-fidelity memorial desktop pet visual asset from the authorized reference photos. The website UI may be soft 2D pixel-inspired, but the generated desktop pet asset must not be pixel art, must not be blocky, and must preserve natural likeness. Generate one clean 2D desktop pet atlas preview. a  Keep the same face, coat texture, fur markings, eye color, body proportions, and familiar gentle presence. Use low-disturbance memorial actions as design intent: idle, sleep, walk, look, sit, tail_wag. Do not add text, logos, humans, room scenery, speech bubbles, tears, halos, medical claims, therapy claims, resurrection framing, chatbot framing, or productivity-assistant elements. Pet display name: rM  z. Visual analysis: species=speciespet, base_color=
base_colorunknown, accent_color=accent_color, eye_color=	eye_color, body_shape=
body_shapez, distinctive_marks=z7. Owner-provided action cues only, not text to render: rI   )rC   rQ  rS  r   rQ   rD   r   !CHROMA_KEY_BACKGROUND_INSTRUCTION)rh   r   r   rV  marks
safe_notesr   r   r   r  k  s2   

 
	






r  c                 C  s|   dt  d| d d  d|dd d|dd	 d
|dd	 d|dd	 d|dd	 dd|  d d  dS )Na;  Create one canonical high-fidelity 2D memorial desktop pet reference from the authorized raw pet photos. Generate exactly one zoomed-out full-body pet, centered, no background scenery. The whole body must fit comfortably inside the image with safe padding; all four paws and tail visible when anatomically present. Use a neutral standing or relaxed side-three-quarter full-body pose suitable as an identity reference for later animation; not a lying, sleeping, curled, or loafing pose. Do not create a headshot, bust, close-up portrait, cropped body, or half-body image. ar  Preserve real likeness: face, coat texture, fur markings, eye color, body proportions, and calm visual identity. This is not pixel art; do not create blocky pixels or an 8-bit style. Do not add text, logos, humans, speech bubbles, halos, tears, medical claims, therapy claims, resurrection framing, chatbot framing, or productivity-assistant elements. Pet display name: rM  z. Visual traits: species=r\  r]  r^  r_  r`  ra  rb  rc  rd  re  rf  .. Owner action cues only, not text to render: rJ     rI   ) CANONICAL_BACKGROUND_INSTRUCTIONrC   rQ   rD   r   r  r   r   r   r    s$   





r  c                 C  s   t  dt  dt  dt  dt  dt  dd| t  d}d	t  d
|  d| dt  dt d|d d  dd|  d d  dS )Nz> subtle idle poses with tiny breathing or head angle variationz4 calm sleeping or drowsy poses, very low disturbancez< gentle walking poses, side-oriented, no speed lines or dustzY calm looking poses with stable body direction and small head or eye changes, not talkingz* seated poses with small posture variationzV visible tail-motion poses where the tail is the main action and the body stays stableidler   r/  rA  rB  rC  z low-disturbance poseszYUsing the canonical reference image as the only pet identity reference, generate exactly z8 separated full-body desktop pet frames for the action 'z'. Action meaning: z. Place the z3 frames in a single horizontal row, evenly spaced. a  Keep the same pet identity in every frame: same face, fur markings, body proportions, eye color, and calm memorial tone. Do not change species, do not add text, logos, humans, scenery, detached effects, speed lines, speech bubbles, tears, halos, medical/therapy claims, resurrection framing, chatbot framing, or productivity-assistant elements. The result must be high-fidelity 2D desktop pet art, not pixel art and not blocky. Pet display name: rM  rj  rJ     rI   )r   rC   rg  rQ   rD   r   )r   rh   r   r   action_guidancer   r   r   r%    s.   	
	
r%  c                 C  s   t | \}}ddd |D }d|d d  d|  dt dt d	t d
t dt d| d| d|dd d|dd d|dd d|dd dd|  d d  dS )NrJ  c                 s  s    | ]}d | V  qdS )z- Nr   )r8   r   r   r   r   rG     s    z-_build_action_strip_prompt.<locals>.<genexpr>z@Create one horizontal animation strip for memorial desktop pet `rM  z
`, state `aF  `. Treat this request as exactly one action-row job in a hatch-pet-style production pipeline. Use the attached canonical base as the only pet identity reference: markings, body type, coat texture, face, eye color, tail visibility, and familiar expression must come from that canonical base. Use the attached layout guide only for slot count, spacing, centering, and padding. Do not copy any visible guide pixels, guide colors, borders, center marks, labels, or background into the result. Do not generate a single large animal portrait. Do not generate one close-up. Output exactly zR separate full-body versions of the same pet in one left-to-right horizontal row. zTreat the row as z invisible equal-width slots: one centered complete pose per slot, evenly spaced, with no overlap, clipping, empty slots, labels, borders, or merged bodies. Leave clear empty space between the z9 pet copies. Use a zoomed-out sprite-sheet camera so all au   complete bodies fit; never fill the canvas with one animal. Identity must stay the same in every frame: preserve the same face, coat texture, fur markings, body proportions, eye color, calm memorial tone, and high-fidelity 2D look from the canonical base. Animation continuity: keep apparent pet scale stable within the row and change pose within each slot. State action: z. State requirements: as   Clean extraction: crisp opaque pet edges, safe padding, no scenery, text, guide marks, checkerboard, shadows, glows, motion blur, speed lines, dust, detached effects, stray pixels, halos, speech bubbles, therapy claims, resurrection framing, chatbot framing, productivity-assistant framing, pixel art, blocky pixels, or 8-bit style. Visual traits from analysis: species=r\  r]  r^  r_  r`  ra  rb  rc  rd  rj  x   rI   )_action_strip_requirementsrQ   r   rg  rC   rD   r   )r   rh   r   r   action_promptrF  requirement_linesr   r   r   r(    s8   	




r(  r,  r*  r0  r1  c                 C  sF  g dg dg dg dg dg dd | dgt }||t|  }d	d
ddddd | | }	|r>| dkr>|dkr>d}
n|rCd}
nd}
dt ddt dt dt dd | d}d}|dkrpddddd d! | d"}d}|d#krd$d%t d&d'd(d)d* | d+}d,|	 d-|d.  d/t d0| | | | d1t d2|
 d3S )4N)u   拖动结束后的短暂停留第 1 帧：四脚站立静止，四只脚自然支撑，背部基本水平，臀部离地；不要坐姿，屁股不要贴地。u   拖动结束后的短暂停留第 2 帧：仍然四脚站立静止，只允许轻微呼吸、眨眼或尾尖小变化；不要坐姿，不要走路，屁股不要贴地。u   拖动结束后的短暂停留第 3 帧：四脚站立静止，身体只做很小的放松变化，臀部仍然离地；不要画成 sit 行的稳定坐姿。u   拖动结束后的短暂停留第 4 帧：回到接近第 1 帧的四脚站立静止，准备 20 秒后才会切换到 sit；不要坐姿，屁股不要贴地。)u   同一睡姿方向的低伏侧躺睡觉，头部固定在画面右侧，身体在画面左侧，鼻尖朝画面右下或右侧；眼睛半闭或闭合，不是坐姿，不要翻转方向。u   同一睡姿方向，头部仍固定在画面右侧，身体仍在画面左侧，完全睡着，眼睛闭合；只允许轻微呼吸变化，不要翻转方向。u   同一睡姿方向，头部仍固定在画面右侧，头枕在前爪上，后腿和尾巴只有小变化，眼睛闭合；不要换到另一侧睡，不要翻转方向。u   同一睡姿方向，头部仍固定在画面右侧，回到接近第 1 帧的低伏侧躺睡姿，耳朵或前爪有小变化，准备循环；不要翻转方向。)ue  右侧侧面正在往右走路。身体横向，头在身体右端，鼻尖、脸和前进方向都朝画面右侧，不要回头看左。画面右侧最前方那只可见前脚大幅向右前方伸出，另一只前脚竖直支撑在肩膀下方；画面左侧可见后脚向左后方伸出，另一只后脚在臀部下方支撑；四只脚尽量可见。u  右侧侧面正在往右走路。身体横向，头在身体右端，鼻尖、脸和前进方向都朝画面右侧，不要回头看左。第 1 帧向前伸出的前脚现在竖直落地支撑在肩膀下方；另一只前脚在胸口下方清楚抬离地面并向右前方摆动；后脚在身体下方交替，其中一只后脚也要抬离地面；脚位必须和第 1 帧不同。uB   本帧由本地流程复制第 1 帧，不会请求模型生成。uB   本帧由本地流程复制第 2 帧，不会请求模型生成。)uK   安静站立或坐着，保持同一身体方向，眼睛自然看前方。uu   只轻微转动头部和视线，眼睛略微向上看，身体方向保持不变，四只脚保持原地不走路。uu   只轻微转动头部和视线，眼睛略微向前或向下看，身体方向保持不变，不要整只猫转身。uN   头部回到接近第 1 帧的自然观察姿态，保持同一身体方向。)ux   同一个坐姿的第 1 帧：坐在地上，屁股和后腿贴地，前爪竖直在身体前方，绝对不是站立。u   同一个坐姿的第 2 帧：仍然坐在地上，屁股和后腿贴地，只允许眨眼、耳朵或头部有很小变化，前爪保持竖直。u   同一个坐姿的第 3 帧：仍然坐在地上，身体不要移动，不要站起来，只允许眨眼、耳朵或头部有很小变化。ur   同一个坐姿的第 4 帧：回到接近第 1 帧的稳定坐姿，屁股和后腿仍然贴地，准备循环。)u   同一侧面或侧后方站姿，身体尽量保持不变，尾巴清楚可见，尾巴尖放在一侧；轻微摆尾即可，不能用头动或身体动代替。u   同一侧面或侧后方站姿，身体尽量保持不变，尾巴从一侧轻微移动到中间或上方；尾巴有可见小变化即可。u{   同一侧面或侧后方站姿，身体尽量保持不变，尾巴轻微摆到另一侧；尾巴有可见小变化即可。u   同一侧面或侧后方站姿，身体尽量保持不变，尾巴回到接近第 1 帧的一侧，准备循环；轻微变化即可。rm  zlow-disturbance poseu   安静待机u   蜷缩或侧躺睡觉u   右侧侧面正在往右走路u   安静观察u   稳定坐姿u   尾巴轻微摆动r/     um   第 2 张参考图只用于保持画风、比例和角色连续性；当前姿态仍按本帧描述生成。 u   第 2 张参考图是同一动作上一帧，只用来保持画风、比例和角色连续性；当前姿态仍按本帧描述生成。 r@   u   idle 是拖动结束后到坐下前的过渡常驻，必须和 sit 明显不同：四脚站立静止，背部基本水平，臀部离地，20 秒后才会切换到 sit；不要坐姿，不要屁股贴地，不要画成正面稳定坐着。u    帧都必须保持同一睡姿方向，只允许呼吸、耳朵、前爪的小变化；任何一帧都不要翻转方向，不要换成另一侧躺。uD  本流程只向模型生成第 1、2 帧，后两帧由本地复制第 1、2 帧形成两帧循环。鼻尖和前进方向始终朝右，任何一帧都不能朝左；第 1、2 帧必须是两个不同的右行脚位，至少一只脚的位置明显不同；不能只平移身体，不能正面坐着，不能趴着不走。u    帧都必须保持同一个坐姿，只允许眨眼、耳朵或头部有很小变化；不要站立，不要四脚撑地走路，不要画成从站立到坐下的过渡。u    帧都必须保持同一身体方向，只允许头部和眼神小幅变化；不要整只猫转身，不要在同一行动画里左右翻转。u    帧保留同一只猫的身份一致性，尾巴清楚可见并有轻微位置变化即可；不要为了夸张摆尾而改变脸、体型、毛色或整体风格。)rn  r   r/  rB  rA  rC  r   u   这是修复候选：上一轮可能出现方向翻转或不像睡觉，本帧必须仍然低伏睡觉且保持同一睡姿方向。u   这是修复候选：上一轮可能出现第 1、2 帧脚步太像或没有抬脚，本帧必须侧面朝右走路，鼻尖和脚步方向都朝右；必须有一只脚清楚抬离地面或明显蹬地。u   这是修复候选：上一轮可能出现整只猫转身或方向变化，本帧必须保持同一身体方向，只允许头部和眼神小幅变化。u   这是修复候选：上一轮可能出现站立或走路，本帧必须明确坐在地上，屁股和后腿贴地，四只脚不能形成站立支撑。u   这是修复候选：上一轮可能尾巴没有变化，本帧让尾巴有轻微可见变化即可，身体尽量保持同一站姿，不要牺牲身份一致性。r@  uB   这是修复候选：本帧必须更清楚地表现指定动作。
   u   这是最终整表 QA 修复候选：必须明显不同于 sit；保持站立或半站立放松，不要坐姿，不要屁股贴地。u%   这是最终整表 QA 修复候选：u    帧睡觉必须保持同一方向，头部都在画面右侧，身体都在画面左侧；不要出现任何镜像、反向或坐姿帧。u  这是最终整表 QA 修复候选：只生成第 1、2 帧关键脚位，后两帧由本地复制；本帧必须右行，头必须在身体右端，鼻尖必须朝画面右侧；第 1、2 帧必须明显不同；不要出现任何朝左、回头看左、正面或坐趴帧。u   这是最终整表 QA 修复候选：必须明显不同于 idle；本帧保持同一身体方向，只允许头部和眼神变化，不要整只猫转身。u   这是最终整表 QA 修复候选：轻微尾巴变化也可以，保留同一只猫的脸、毛色、体型和站姿，不要牺牲身份一致性。)rn  r   r/  rA  rC  us   这是最终整表 QA 修复候选：本帧必须更清楚地表现指定动作，并和其他动作行区分开。u0   基于第 1 张参考图，生成同一角色的
   动画第 r    / u    帧。u>  保留主要外貌特征：脸、毛色、花纹、体型、眼睛颜色、真实或半写实毛发质感。不要换成另一只猫，不要改成卡通、线稿、矢量插画、贴纸、玩具或吉祥物。保持相同镜头距离、相同视觉体量和相近安全边距，不要让角色突然变大或变小。u   只输出一个完整单帧，只出现一个角色；不要动作条、拼图、网格、多个角色、文字或编号。完整身体可见，居中，有安全边距。不要生成透明像素。us   不要场景、地面、阴影、速度线、特效、对白框、医疗/治疗/复活/聊天/效率工具元素。)rC   r   r+   #ACTION_FRAME_BACKGROUND_INSTRUCTION)r   r,  r*  rh   r   r   r0  rp  phaseaction_titleprevious_frame_lineaction_constraintrepair_constraintfinal_repair_constraintr   r   r   r2    s   %&
r2  c              	   C  s<   ddddddd | d}d	| d
|d  dt dt d	S )Nu!   安静待机，轻微呼吸起伏u$   蜷缩或侧躺睡觉，身体低伏u<   右侧侧面正在往右走路，身体横向、面部朝右uK   安静观察，保持同一身体方向，只有头部和眼神轻微变化uW   坐在地上的稳定坐姿，屁股和后腿贴地，前爪竖直，绝对不是站立u!   尾巴清楚可见，轻微摆动rm  zquiet low-disturbance poseu-   基于这张参考图，生成同一角色的rw  r   rx  u    帧。保留主要外貌特征和真实或半写实毛发质感。不要换成另一只猫，不要改成卡通、线稿、矢量插画、贴纸、玩具或吉祥物。u   只输出一个完整单帧，只出现一个角色；不要文字、编号、动作条、拼图、网格、场景、地面、阴影或透明像素。)rC   r   ry  )r   r,  r*  rh   r   r   rp  r   r   r   r5  8  s   
	r5  tuple[str, list[str]]c                 C  sd   ddt  ddgfdddgfdg d	fd
g dfdddgfdg dfd}|| ddt  dgfS )Nz`Calm low-distraction resting loop with subtle breathing, tiny blink, and slight head or body bobzCKeep the pet essentially in the same calm baseline pose across all z frames.zCDo not show walking, running, jumping, talking, or emotional drama.zLQuiet sleeping loop with lying, curled, or clearly low sleeping body posturezXEvery frame must read as sleep: lying, curled, or low relaxed body, not sitting upright.zfEyes should be closed or drowsy and the body should stay low with tiny breathing or ear/paw variation.z1Gentle walking loop in side-oriented body posture)zXEvery frame must read as walking, with side-oriented body and alternating paw positions.zADo not reuse the canonical front-facing sitting or standing pose.z1No speed lines, dust, ground shadows, or scenery.zSGentle looking-around loop with stable body direction and head or eye angle changes)zWKeep the same body orientation in every frame; only the head and eyes may shift gently.zJDo not turn the whole body around or change body direction within the row.z=Do not make the pet talk, emote dramatically, or add symbols.z9Settled seated loop with small posture and head variationzAA seated pose is allowed here, but frames must still vary gently.z0Preserve the same body proportions and markings.zRVisible tail small-motion loop, turning the body if needed so the tail can be seen)zSThe tail must be visible in most frames unless the real animal has no visible tail.zFShow left, center, right, and return tail positions through pose only.z,Do not add motion marks or detached effects.rm  z0Low-disturbance memorial desktop pet action loopzPreserve identity and generate z$ clearly separated full-body frames.rD  rE  r   r   r   rr  R  s>   
		/rr  c                 C  s   dd l }|| d S )Nr   r   )hashlibsha256r   	hexdigest)r'   r  r   r   r   r    s   r  rj   rk   rl   c                 C  s    t | d}d| d| S )Nasciizdata:z;base64,)base64	b64encoder   )rj   rl   encodedr   r   r   r     s   r   rq   c                 C  sz   t | } zt| }W n tjy!   d d | d d dgd}Y nw t|ts5d d t|d d dgd}|dg  |S )Nr   non_json_review)scorepassedr   risksunexpected_review_shaper  )_extract_json_textr   r   r   rQ  rR  r   
setdefault)rq   r   r   r   r   r     s   
r   c                 C  s   |   }|dr4| }|r|d dr|dd  }|r-|d   dr-|d d }d|  }d|v rNd|v rN|d}|dd }||| S |S )Nz```r   r   
{})rD   r6   r   rQ   findrfind)rq   strippedlinesstartendr   r   r   r    s   

r  r   c                 C  s  |  dd}zt|}W n ttfy   d}Y nw |  dg }t|ts*t|g}t|  ddd d t|  ddd d t|  d	d
d d t|  ddd d t|  ddd d dd |d d D tdtd|t	|  ddt|  ddd d d	S )N
confidenceg      ?rV  r\  r]  (   r_  grayrb  whiterd  greenrf  roundedrM  c                 S  s   g | ]
}t |d d qS )NrM  rW  rX  r   r   r   r    s    z-_normalize_photo_analysis.<locals>.<listcomp>rZ  r   g      ?safe_for_generationTuser_visible_summaryu6   已提取适合生成低打扰桌宠的视觉特征。rN  )	r\  r_  rb  rd  rf  rV  r  r  r  )
rC   r   r   r   rQ  rS  r   r   r   r1  )r   r  rh  r   r   r   r     s&   

r   )r'   r   r   r   r   )r/   r0   r.   r1   r   r   )r   r   r   r   )r   r   r9   r   r   r   r   r   )r   r   r9   r   r   r   r   r   )r   r   r   r   r   r   r   r   )r   r   r   r   )r   r   r   r   )r   r	   r   r   )r   rm   r   r   )r   r   r   rY   )rh   r   r   r   r   rm   r   r   )
r   r   rh   r   r   r   r   rm   r   r   )r   r   r,  r   r*  r   rh   r   r   r   r   rm   r0  r1  r   r   )r   r   r,  r   r*  r   rh   r   r   r   r   rm   r   r   )r   r   r   r  )rj   rk   rl   r   r   r   )rq   r   r   rm   )rq   r   r   r   )r   rm   r   rm   ):
__future__r   r  r   rL   r;  dataclassesr   pathlibr   typingr   r   urllib.parser   r   urllib.errorr	   r
   urllib.requestr   r   packages.pet_package_schemar   RuntimeErrorr   r   r   r   rT   rU   rK   r   r   r   r   r   r   r7  r   r9  r  r  rg  ry  rl  r  r  r%  r(  r2  r5  rr  r  r   r   r  r   r   r   r   r   <module>   sn    
 
[



W  
P










j

9


