o
    RFjż                     @  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 d dlmZmZ d dlmZmZ d dl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eG dd dZdiddZdjdkddZdjdlddZ G dd  d Z!G d!d" d"Z"dmd&d'Z#dnd,d-Z$dod1d2Z%dpd4d5Z&dqd9d:Z'drd=d>Z(drd?d@Z)dsdBdCZ*dtdEdFZ+dudHdIZ,dvdKdLZ-dwdNdOZ.dxdPdQZ/dydUdVZ0dzd[d\Z1d d]d{d^d_Z2d|dadbZ3d}dedfZ4d~dgdhZ5dS )    )annotationsN)	dataclass)RemoteDisconnected)Path)AnyCallable)	HTTPErrorURLError)quote)Requesturlopen)DEFAULT_FRAMES_PER_ACTIONc                   @     e Zd ZdS )APIMartConfigErrorN__name__
__module____qualname__ r   r   @/opt/sixxie/releases/current/services/ai/apimart_image_client.pyr          r   c                   @  r   )APIMartRequestErrorNr   r   r   r   r   r      r   r   c                   @  sn   e Zd ZU ded< ded< ded< dZded< dZded	< d
Zded< dZded< edddZ	dddZ
dS )APIMartConfigstrapi_keybase_urlmodel1k
resolution      @floatpoll_interval_seconds      @timeout_seconds     V@request_timeout_secondsreturnc                 C  
   t | jS Nmask_secretr   selfr   r   r   masked_api_key$      
zAPIMartConfig.masked_api_keyc              	   C  s&   d| j d| jd| jd| jd	S )NzAPIMartConfig(api_key=, base_url=, model=z, resolution=))r-   r   r   r   r+   r   r   r   __repr__(   s   zAPIMartConfig.__repr__Nr&   r   )r   r   r   __annotations__r   r!   r#   r%   propertyr-   r2   r   r   r   r   r      s   
 r   c                   @  sJ   e Zd ZU ded< ded< ded< dZded< edd	d
ZdddZdS )APIMartReviewConfigr   r   r   r         N@r    r%   r&   c                 C  r'   r(   r)   r+   r   r   r   r-   9   r.   z"APIMartReviewConfig.masked_api_keyc                 C  s   d| j d| jd| jdS )NzAPIMartReviewConfig(api_key=r/   r0   r1   )r-   r   r   r+   r   r   r   r2   =   s   zAPIMartReviewConfig.__repr__Nr3   )r   r   r   r4   r%   r5   r-   r2   r   r   r   r   r6   2   s   
 r6   valuer   r&   c                 C  s.   t | dk rdS | d d  d| dd   S )N   z***   z...)len)r8   r   r   r   r*   F   s   r*   
.env.localenv_path
str | Pathc                 C  s   t t| }i |dd tj D }|dd }|dd d}|dd	 }|r5|r5|s9td
t	||||dd pFdt
|ddt
|ddt
|dddS )Nc                 S      i | ]\}}| d r||qS APIMART_
startswith.0keyr8   r   r   r   
<dictcomp>P       z'load_apimart_config.<locals>.<dictcomp>APIMART_API_KEY APIMART_BASE_URLhttps://api.apimart.ai/v1/APIMART_MODEL_IMAGEgpt-image-2z]APIMart config is incomplete. Set APIMART_API_KEY, APIMART_BASE_URL, and APIMART_MODEL_IMAGE.APIMART_IMAGE_RESOLUTIONr   #APIMART_IMAGE_POLL_INTERVAL_SECONDSr   APIMART_IMAGE_TIMEOUT_SECONDSr"   %APIMART_IMAGE_REQUEST_TIMEOUT_SECONDSr$   )r   r   r   r   r!   r#   r%   )_read_env_filer   osenvironitemsgetstriprstripr   r   
_env_floatr>   
env_valuesmergedr   r   r   r   r   r   load_apimart_configL   s0   


r`   c                 C  s   t t| }i |dd tj D }|dd }|dd d}|dd }|r5|r5|s9td	t	|||t
|d
ddS )Nc                 S  r@   rA   rC   rE   r   r   r   rH   l   rI   z.load_apimart_review_config.<locals>.<dictcomp>rJ   rK   rL   rM   rN   APIMART_MODEL_REVIEWzeAPIMart review config is incomplete. Set APIMART_API_KEY, APIMART_BASE_URL, and APIMART_MODEL_REVIEW.&APIMART_REVIEW_REQUEST_TIMEOUT_SECONDSr7   )r   r   r   r%   )rU   r   rV   rW   rX   rY   rZ   r[   r   r6   r\   r]   r   r   r   load_apimart_review_configh   s*   
rc   c                   @  s   e Zd ZdZddeejejdddddd
dTddZe		dUed dVd$d%Z
d&d'dWd/d0Zd1d2d&d3dXd;d<Zd1d2d&d=dYd>d?Zd1d@dAdZdCdDZd[dHdIZd\dLdMZd]dOdPZd^dRdSZd2S )_APIMartImageGenerationClientapimartrP   r   r   r"   r$      )
r   r   openersleepclockr!   r#   r%   reference_limitmax_poll_retriesr   r   r   r   r   rg   Callable[..., Any]rh   Callable[[float], Any]ri   Callable[[], float]r!   r    r#   r%   rj   intrk   r&   Nonec                C  s   || _ |d| _|| _|| _|| _|| _|| _tdt	|| _
tdt	|	| _tdt	|
| _tdtt|d| _tdt|| _d S )NrN           g      $@r         r   )r   r[   r   r   r   rg   rh   ri   maxr    r!   r#   r%   minro   rj   rk   )r,   r   r   r   r   rg   rh   ri   r!   r#   r%   rj   rk   r   r   r   __init__   s   z%APIMartImageGenerationClient.__init__r=   rg   r>   r?   'APIMartImageGenerationClient'c             
   C  s.   t |}| |j|j|j|j||j|j|jdS )N)r   r   r   r   rg   r!   r#   r%   )r`   r   r   r   r   r!   r#   r%   clsr>   rg   configr   r   r   from_env   s   z%APIMartImageGenerationClient.from_envz	1024*1024)sizepet_namenotesanalysisdict[str, Any]imageslist[dict[str, Any]]r}   c                C  sF   |st dt|||d}dd |d | j D }| j||t|dS )Nz;at least one image is required for APIMart image generation)r~   r   r   c                 S  s   g | ]}t |d  |d qS )bytesmime)_to_data_url)rF   imager   r   r   
<listcomp>   s    zGAPIMartImageGenerationClient.generate_canonical_pet.<locals>.<listcomp>prompt
image_urlsr}   )
ValueError_build_canonical_pet_promptrj   _generate_image_normalize_size)r,   r~   r   r   r   r}   r   r   r   r   r   generate_canonical_pet   s   	z3APIMartImageGenerationClient.generate_canonical_petr   N)candidate_indexprevious_frame_image_bytesr}   actionframe_indexr   canonical_image_bytesr   r   bytes | Nonec       
      	   C  s   |st d|dk s|tkrt dtd  t|	o|dk}t|||||||d}t|dg}|r:|t|	d | j||t|
dS )	Nz9canonical image is required for APIMart action generationr   "frame_index must be between 0 and rr   walkr   r   r   r~   r   r   has_previous_frame	image/pngr   )r   r   bool_build_action_frame_promptr   appendr   r   )r,   r   r   r   r~   r   r   r   r   r   r}   attach_previous_framer   r   r   r   r   generate_action_frame   s,   	z2APIMartImageGenerationClient.generate_action_frame)r   r   r}   c             	   C  sd   |dk s|t krtdt d  t||||||dd}	g }
|r(|
t|d | j|	|
t|dS )Nr   r   rr   Fr   r   r   )r   r   r   r   r   r   r   )r,   r   r   r   r~   r   r   r   r}   r   r   r   r   r   generate_action_frame_minimal   s&   	z:APIMartImageGenerationClient.generate_action_frame_minimalz1280*720)r   r}   layout_guide_image_bytesc       	         C  sR   |st d|st dt|||||d}
t|dt|dg}| j|
|t|	dS )Nz?canonical image is required for APIMart action strip generationzBlayout guide image is required for APIMart action strip generation)r   r   r~   r   r   r   r   )r   _build_action_strip_promptr   r   r   )r,   r   r   r~   r   r   r   r   r   r}   r   r   r   r   r   generate_action_strip  s&   z2APIMartImageGenerationClient.generate_action_stripr   r   	list[str]c             	   C  s   |   }| j|d|| jd}|r||d< | | j d|}t|}| |}t|}	| |	}
t	t
d|   | d}t|trG|di ni }t|}||d< d	d
| j||
d|t|dS )Nrr   )r   r   nr}   r   r   z/images/generationsrq   rf   dataobserved_duration_secondsokre   r   )statusproviderr   
request_idimage_bytes
image_mimeusageprompt_sha256)ri   r   r   
_post_jsonr   _extract_task_id
_poll_task_extract_completed_image_url_download_imageroundrt   
isinstancedictrY   _safe_usage_sha256_text)r,   r   r   r}   
started_atpayload	submittedtask_id	completed	image_urlr   r   r   r   r   r   r   r   1  s6   

z,APIMartImageGenerationClient._generate_imageurlr   c                 C  s,  t |d}t| jd D ]}t||dd| j ddd}z'| j|| jd}t 	|
 dW  d    W   S 1 sAw   Y  W q ty\ } z	td	|j |d }~w ty } z|| jk rw| jrq| | j W Y d }~qtd
|d }~w t jy } ztd|d }~ww td
)Nutf-8rr   POSTBearer application/jsonAuthorizationzContent-Typer   methodheaderstimeoutz'APIMart image request failed with HTTP z8APIMart image request failed before receiving a responsez)APIMart image response was not valid JSON)jsondumpsencoderangerk   r   r   rg   r%   loadsreaddecoder   r   coder	   r!   rh   JSONDecodeError)r,   r   r   bodyattemptrequestresponseexcr   r   r   r   V  s:   
	*


z'APIMartImageGenerationClient._post_jsonr   c              
   C  s^  t  | j }| j dt| d}d}	 t|ddd| j id}z$| j|| jd	}t	
| d
}W d    n1 s@w   Y  W n tyu } z#|d7 }|| jkrlt  |k rl| jrf| | j W Y d }~qtd|d }~w ty } z	td|j |d }~w ty } z#|d7 }|| jkrt  |k r| jr| | j W Y d }~qtd|d }~w t	jy } ztd|d }~ww t|tr|di ni }	t|	tr|	dnd }
|
dkr|S |
dv rd}t|	tr|	dni }t|trt|dp	d}td|
 d|d d  t  |kr$td| jr.| | j q)Nz/tasks/z?language=zhr   TGETr   r   )r   r   r   r   rr   zFAPIMart task polling connection was closed before receiving a responsez&APIMart task polling failed with HTTP z7APIMart task polling failed before receiving a responsez(APIMart task response was not valid JSONr   r   r   >   failed	cancelledrK   errormessagezAPIMart image task :    zAPIMart image task timed out)time	monotonicr#   r   r
   r   r   rg   r%   r   r   r   r   r   rk   r!   rh   r   r   r   r	   r   r   r   rY   r   )r,   r   deadlinetask_urltransient_errorsr   r   r   r   r   r   r   r   r   r   r   r   q  sh   



z'APIMartImageGenerationClient._poll_taskr   c                 C  s   d}t | jd D ]\}z!| j|| jd}| }	 W d    W  nC1 s&w   Y  W q	 tyA } z	td|j |d }~w tye } z|| jk r\| j	rV| 
| j	 W Y d }~q	td|d }~ww |sltd|S )N    rr   r   z2APIMart generated image download failed with HTTP zCAPIMart generated image download failed before receiving a responsez*APIMart generated image download was empty)r   rk   rg   r%   r   r   r   r   r	   r!   rh   )r,   r   r   r   r   r   r   r   r   r     s*   &

z,APIMartImageGenerationClient._download_image)r   r   r   r   r   r   r   r   rg   rl   rh   rm   ri   rn   r!   r    r#   r    r%   r    rj   ro   rk   ro   r&   rp   r=   )r>   r?   rg   rl   r&   rx   )r~   r   r   r   r   r   r   r   r}   r   r&   r   )r   r   r   ro   r   ro   r~   r   r   r   r   r   r   r   r   r   r   r   r}   r   r&   r   )r   r   r   ro   r   ro   r~   r   r   r   r   r   r   r   r}   r   r&   r   )r   r   r   ro   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   r   r   r&   r   )r   r   r&   r   )r   r   r&   r   )r   r   r   r   r   r   rh   r   rv   classmethodr|   r   r   r   r   r   r   r   r   r   r   r   r   rd      sF    *$
"
%
/rd   c                   @  sX   e Zd ZdZeddd%ddZe	d&edd'ddZd(ddZd)dd Z	d*d"d#Z
d$S )+APIMartVisualReviewClientre   r7   )rg   r%   r   r   r   r   rg   rl   r%   r    r&   rp   c                C  s2   || _ |d| _|| _|| _tdt|| _d S )NrN   r   )r   r[   r   r   rg   rt   r    r%   )r,   r   r   r   rg   r%   r   r   r   rv     s
   	z"APIMartVisualReviewClient.__init__r=   rw   r>   r?   'APIMartVisualReviewClient'c                C  s"   t |}| |j|j|j||jdS )N)r   r   r   rg   r%   )rc   r   r   r   r%   ry   r   r   r   r|     s   z"APIMartVisualReviewClient.from_envr~   r   r   r   r   r   c          	      C  s   t ||}| jdddddd| d| dt 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}dd| j|dt|dS )Nsystema-  You review one generated action row for a memorial desktop pet. Return compact JSON only. Check same pet identity, full-body visibility, clean background removal readiness, and action semantics. Reject wrong species, unrelated simplified animals, cropped or broken bodies, or unclear requested action.rolecontentusertextz:Please review one generated action row. Pet display name: z. Action to verify: z. The zU frames must look like the same pet identity and 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).typer   r   r   r   r   r   r   messagestemperaturechoicesr   r   rK   r   re   idr   r   r   response_idreview)r   r   r   "_action_visual_review_requirements_post_chat_completionsrY   _parse_review_content)	r,   r~   r   r   r   data_urlr   rawr   r   r   r   review_action_frames  sF   
	
""z.APIMartVisualReviewClient.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}dd| j|dt|dS )Nr   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.r   r   r   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).r   r   r   r   r   r   r   r   r   rK   r   re   r   r   )r   r   r   r   rY   r   )r,   r~   r   r   r   r   r  r   r   r   r   review_contact_sheet  s>   
	(
,"z.APIMartVisualReviewClient.review_contact_sheetr   c              
   C  s   t |d}tt| j|dd| j ddd}z&| j|| jd}t 	|
 dW  d    W S 1 s9w   Y  W d S  tyU } z	td|j |d }~w tye } ztd	|d }~w t jyv } ztd
|d }~ww )Nr   r   r   r   r   r   r   z(APIMart review request failed with HTTP z9APIMart review request failed before receiving a responsez*APIMart review response was not valid JSON)r   r   r   r   _chat_completions_urlr   r   rg   r%   r   r   r   r   r   r   r	   r   )r,   r   r   r   r   r   r   r   r   r   I  s.   
	(

z0APIMartVisualReviewClient._post_chat_completionsN)r   r   r   r   r   r   rg   rl   r%   r    r&   rp   r   )r>   r?   rg   rl   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   r   r   r   rv   r   r|   r  r  r   r   r   r   r   r     s    

5>r   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#=rr   "')exists	read_text
splitlinesrZ   rD   split)r  valuesraw_linelinerG   r8   r   r   r   rU   _  s   rU   r  rG   defaultr    c              	   C  s6   zt t| || W S  ttfy   | Y S w r(   )r    r   rY   rZ   	TypeErrorr   )r  rG   r  r   r   r   r\   l  s
   r\   r   r   r   c                 C  s   d| dt | d S )Nzdata:z;base64,ascii)base64	b64encoder   )r   r   r   r   r   r   s  s   r   r}   c                 C  s   t | pd  }|sdS d|v s|dkr|S d|v r"|dd}d|v rZ|dd\}}z
t|}t|}W n tyC   | Y S w ||krJdS t||}||  d||  S |S )NrK   z1:1:auto*xrr   )r   rZ   lowerreplacer  ro   r   _gcd)r}   clean
width_textheight_textwidthheightratior   r   r   r   w  s(   
r   leftro   rightc                 C  s$   |r|| | } }|st t| dS )Nrr   )rt   abs)r%  r&  r   r   r   r    s   r  r   r   c                 C  s   |  ddvrtd|  d |  d}t|tr.|r.t|d tr+|d  dnd }nt|tr>| dp<| d}nd }|sFtdt|S )	Nr   )N   z$APIMart image request returned code r   r   r   r   z,APIMart image request did not return task_id)rY   r   r   listr   r   )r   r   r   r   r   r   r     s   
"
r   c                 C  s   t | tr| di ni }t |tr|di ni }t |tr%|dg ng }t |trU|D ]&}t |tr:|dnd }t |trK|rKt|d   S t |trT|  S q.td)Nr   resultr   r   r   z3APIMart completed task did not include an image URL)r   r   rY   r)  r   r   )r   r   r*  r   r   urlsr   r   r   r     s   

r   r   c                 C  s.   |  d|  d|  d|  d|  ddS )Ncostcredits_costactual_timeestimated_timeprogress)r,  r-  r.  r/  r0  )rY   )r   r   r   r   r     s   r   r   c                 C  s   dd l }|| d S )Nr   r   )hashlibsha256r   	hexdigest)r   r1  r   r   r   r     s   r   r   c                 C  s"   |  d}|dr|S | dS )NrN   z/chat/completions)r[   endswith)r   r  r   r   r   r    s   


r  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.)rh   r   looksittail_wagzMHard rules: preserve the same pet identity and reject wrong action semantics.r   rY   r   requirementsr   r   r   r     s   

	
r   r   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 )N   non_json_review)scorepassedr   risksunexpected_review_shaper?  )_extract_json_textr   r   r   r   r   r   
setdefault)r   	json_textparsedr   r   r   r     s   
r   c                 C  s   t | pd }|dr8| }|r |d dr |dd  }|r1|d  dr1|d d }d| }d|v rRd|v rR|d}|dd }||| S |S )	NrK   z```r   rr   
{})r   rZ   rD   r  joinfindrfind)r   strippedlinesstartendr   r   r   rA    s   

rA  r~   r   r   c                 C  sz   d |  d d }d| d d  d|dd d|d	d
 d|dd
 d|dd
 d|dd
 d| dS )N    a2  Create one high-fidelity 2D memorial desktop pet from the authorized reference images. Generate exactly one complete full-body pet on a plain white background, centered with safe padding. Preserve the same pet identity, face, coat texture, fur markings, body proportions, eye color, and calm familiar presence. This is not pixel art, not a sticker, not a toy, and not a mascot redesign. Do not add text, logos, humans, room scenery, speech bubbles, tears, halos, therapy claims, resurrection framing, chatbot framing, or productivity elements. Pet display name: P   . Visual traits: species=speciespet, base_color=
base_colorunknown, accent_color=accent_color, eye_color=	eye_colorz, body_shape=
body_shape.. Owner action cues only, not text to render: .)rI  rZ   r  rY   )r~   r   r   
safe_notesr   r   r   r     s"   





	r   r   r   r   r   c                 C  s"  dddt  ddt  dddd	| d
}t| |}|r'| dkr'|dkr'd}	n|r0| dkr0d}	n|r5d}	nd}	|dkrB| dkrBd}
n|dkrM| dkrMd}
n	|dkrTd}
nd}
d|  d|d  dt  d| d| |
 |	 d|d d  d|dd d |d!d" d#d$|  d d%  d&S )'Nzspost-drag temporary idle: standing on all four paws, back mostly horizontal, hips off the ground, not a seated posezsame-direction calm sleeping pose, very low disturbance; all frames must keep the same left-to-right sleeping orientation, with the head on the same side and the body/tail on the same sidezright-facing side-view gentle walking pose; this pipeline generates only frames 1 and 2, then locally copies them as frames 3 through zW. Frames 1 and 2 must be visibly different paw phases, not just a shifted standing posezFquiet looking-around frame; keep the same body orientation across all z4 look frames; only the head and eyes change slightlyz.stable seated pose with tiny posture variationzThe tail is the main action. Use the same side-view or rear-three-quarter body pose, keep the body almost still, and make the tail clearly visible in every frameidlerh   r   r5  r6  r7  zlow-disturbance action poser      zZUse the second reference image only for visual continuity with the previous action frame. rh   zUse the second reference image as a strict direction lock for sleep: keep the same head side, body side, baseline, and lying orientation; do not mirror, flip, rotate, sit up, or change to the opposite side. rK   r   zThis is a repair candidate after gait QA failure: previous images looked like the same paw stance. Make the current frame's front-leg and hind-leg phase unmistakably different, with one lifted paw visibly off the ground. a>  This is a sleep-direction repair candidate after final QA found a flipped sleep frame. Every sleep frame must stay in the exact same sleeping direction: head remains on the right side of the body, body and tail remain on the left side, and the pet stays low/lying. Do not create any mirrored or opposite-facing frame. z?This is a repair candidate; make the requested action clearer. zGGenerate one complete 2D memorial desktop pet action frame for action 'z	', frame rr    of z. Action meaning: z. ab  Keep the same pet identity as the reference image: same face, coat markings, body proportions, eye color, and calm tone. Keep the same camera distance, same apparent body size, and similar safe padding as the reference image and the other action frames. Use a flat pure blue #0000FF background for local chroma-key removal. Do not create transparent pixels. Only one full-body pet should appear, centered with safe padding. No grid, no text, no labels, no shadows, no scenery, no speech bubble. Do not imply the pet is alive, conscious, revived, therapeutic, chatty, or productivity-related. Pet display name: rR  rS  rT  rU  rV  rW  rX  r^  rP  x   r_  )r   rY   _action_frame_guidancerI  rZ   r  )r   r   r   r~   r   r   r   action_guidanceframe_guidanceprevious_frame_linerepair_liner   r   r   r     s^   





r   )r   c           	      C  s   t | \}}ddd |D }d}|dkr d}| dkr |d7 }| dkr(|d	7 }d
|d d  d|  d| 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 )!NrP  c                 s  s    | ]}d | V  qdS )z- Nr   )rF   r  r   r   r   	<genexpr>f  s    z-_build_action_strip_prompt.<locals>.<genexpr>rK   r   zThis is a repair candidate after local action QA rejected the previous strip. Make the requested action visibly clearer while preserving the same identity and stable scale. r   zFor walk, frames 1 and 2 must show clearly different paw phases and a side-oriented walking body, not only a translated standing pose. aP  Walk strip hard rule: create a two-pose walk loop. Slots 1 and 3 must show the same first key pose: right-facing side view, front paw reaching forward, hind paw stretching back. Slots 2 and 4 must show the same second key pose: same right-facing side view, clearly opposite paw phases, one front paw lifted under the chest and one hind paw passing under the body. Keep body size, head, markings, and baseline stable; change paw placement enough to read at desktop size. Do not show four near-identical standing poses, do not merely translate the same silhouette, and do not change direction. z@Create one horizontal animation strip for memorial desktop pet 'rR  z
', state 'z'. z:Use the first attached image only as the layout guide for ab   equal slots, spacing, centering, and safe padding. Use the second attached image as the only pet identity reference: face, coat markings, body proportions, eye color, tail visibility, and familiar calm expression must stay consistent. Do not copy layout guide pixels, colors, borders, center marks, labels, or background into the result. Output exactly zo separate full-body versions of the same pet in one left-to-right horizontal animation strip. Treat the row as z invisible equal-width slots: one centered complete pose per slot, evenly spaced, no overlap, no clipping, no empty slots, no labels, no borders, and no merged bodies. Use a zoomed-out sprite-sheet camera so all a   complete bodies fit; never fill the canvas with one large animal. Use a flat pure blue #0000FF background for local chroma-key removal. Do not create transparent pixels. Identity must stay the same in every frame: preserve 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=rT  rU  rV  rW  rX  rY  rZ  r[  r\  r^  re  r_  )_action_strip_requirementsrI  r   rY   rZ   r  )	r   r   r~   r   r   action_promptr:  requirement_linesrj  r   r   r   r   ]  sP   	



r   tuple[str, list[str]]c                 C  sp   ddt  ddgfdddt  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.zAll zb frames must keep the same sleeping direction: head on the right side, body/tail on the left side.zRDo not mirror, flip, rotate, or switch the side the pet is lying on between slots.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.ra  z0Low-disturbance memorial desktop pet action loopzPreserve identity and generate z$ clearly separated full-body frames.r8  r9  r   r   r   rl    sB   


		1rl  phasestuple[str, ...]c                 C  st   |t |  }| | }d|d  dt |  }||r-d|d  dt |t |d   S d|d  dt d| S )NzFrame rr   rd  r   )r<   rD   r   )rp  r   phase_indexphaselegacy_prefixr   r   r   _frame_phase  s   
$ru  c                 C  s   | dkrd}t ||d dt d S | dkr d}t ||d S | d	kr-d
}t ||d S | dkr:d}t ||d S | dkrGd}t ||d S dS )Nrb  )zpstanding on all four paws right after dragging stops; back mostly horizontal, hips off the ground, not sitting. zstill standing on all four paws, with only tiny breathing, blink, or tail-tip variation; not walking and not seated, hips off the ground. zstanding on all four paws with a small relaxed body variation; back remains mostly horizontal, hips off the ground, do not sit down. zvreturns near frame 1's all-fours standing rest so the loop can hold before switching to sit later; not a seated pose. zsThis idle row is the post-drag temporary state before sit; it must look clearly different from the seated sit row. zAcross all a   idle frames, keep the same body facing direction, same baseline, same silhouette width and height, same camera distance, and same safe padding; only tiny breathing, blink, ear, or tail-tip motion should change. Do not switch between side view and front view. r7  )zZtail is clearly visible and curves to one side; body and head stay calm and nearly still. zMtail moves toward the center or upward; body pose stays the same as frame 1. zOtail clearly curves to the opposite side; body pose stays the same as frame 1. z]tail returns toward the first-frame side so the loop can continue; body pose stays the same. zADo not replace the tail wag with head movement or body movement. rh   )z[low lying or curled sleeping pose, head on the right side, body and tail on the left side. z|same sleeping direction as frame 1 with only tiny breathing, ear, or paw variation; head still right, body/tail still left. zisame sleeping direction as frame 1 with a subtle breathing or drowsy variation; do not mirror or rotate. zWreturn near frame 1 so the sleep loop is calm; head still right, body/tail still left. zHard rule: never switch sleep direction between frames, never place the head on the opposite side, never sit upright, and never use a front-facing pose. r5  )zIcalm neutral look; keep the same body facing direction as the reference. zOhead and eyes look slightly upward while the body orientation stays unchanged. z\head and eyes look slightly forward or downward while the body orientation stays unchanged. zOhead returns near the neutral look while the body orientation stays unchanged. zVDo not turn the whole body around or change the body facing direction between frames. r   )zright-facing side view; the visible front paw on the right reaches far forward; the opposite/front support paw is vertical under the shoulder; the visible hind paw on the left stretches backward; the other hind paw supports under the hip. zright-facing side view; the forward front paw from frame 1 is now vertical and planted under the shoulder; the other front paw is visibly lifted off the ground under the chest, moving forward; hind paws pass under the body with one hind paw lifted. zWcreated locally by copying frame 1; this frame should not be requested from the model. zWcreated locally by copying frame 2; this frame should not be requested from the model. a  Only frames 1 and 2 are model-generated key poses. Make frames 1 and 2 clearly different so the local two-frame loop reads as walking. Exaggerate the paw spacing slightly for small desktop-sprite readability while keeping the same realistic pet identity. Do not keep both generated frames in the same standing pose; this must be a readable two-pose walk loop, not just a shifted standing pose. rK   )ru  r   )r   r   rp  r   r   r   rf    s6   
rf  )r8   r   r&   r   r   )r>   r?   r&   r   )r>   r?   r&   r6   )r  r   r&   r  )r  r  rG   r   r  r    r&   r    )r   r   r   r   r&   r   )r}   r   r&   r   )r%  ro   r&  ro   r&   ro   )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   r&   r   )r~   r   r   r   r   r   r&   r   )r   r   r   ro   r   ro   r~   r   r   r   r   r   r   r   r&   r   )r   r   r   ro   r~   r   r   r   r   r   r&   r   )r   r   r&   ro  )rp  rq  r   ro   r&   r   )r   r   r   ro   r&   r   )6
__future__r   r  r   rV   r   dataclassesr   http.clientr   pathlibr   typingr   r   urllib.errorr   r	   urllib.parser
   urllib.requestr   r   packages.pet_package_schemar   RuntimeErrorr   r   r   r6   r*   r`   rc   rd   r   rU   r\   r   r   r  r   r   r   r   r  r   r   rA  r   r   r   rl  ru  rf  r   r   r   r   <module>   s^    
  5 
,














N
7
;	