Ë
    áß²jz5  ã                  óÌ  — U d Z 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
mZmZmZmZ ddlmZ ddlmZ  ej&                  e«      ZdZd	ed
<   dZdZd	ed<   dZ G d„ de«      Zdddœd(d„Zdddœd)d„ZdddddœZdddddddœ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d*d„Zdd ddd!œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d+d"„Z d#d d d d d$œ	 	 	 	 	 	 	 	 	 	 	 	 	 d,d%„Z! G d&„ d'e«      Z"ddl#Z#ddl$Z$ddl%Z%y)-u  Video Generation Provider ABC.

Providers register via ``PluginContext.register_video_gen_provider()`` and live
in ``<repo>/plugins/video_gen/<name>/`` (built-in) or
``~/.hermes/plugins/video_gen/<name>/``; mirrors ``agent/image_gen_provider.py``.
One tool covers text-to-video and image-to-video: ``image_url`` present routes to
the provider's image-to-video endpoint. Video edit/extend are deliberately NOT
exposed â€” backends are too inconsistent for one unified tool.

Response shape (:func:`success_response` / :func:`error_response`): ``success``,
``video`` (URL or absolute path), ``model``, ``prompt``, ``modality``
("text" | "image"), ``aspect_ratio``, ``duration`` (seconds, 0 if n/a),
``provider``; plus ``error`` / ``error_type`` only when ``success`` is False.
é    )ÚannotationsN)ÚPath)ÚAnyÚDictÚListÚOptionalÚTuple)Úprovider_media)ÚCatalogProviderBase)ú16:9z9:16z1:1z4:3z3:4z3:2z2:3z21:9zTuple[str, ...]ÚCOMMON_ASPECT_RATIOSr   )Ú480pÚ540pÚ720pÚ768pÚ1080pÚCOMMON_RESOLUTIONSr   c                  ó‚   — e Zd ZdZdd„Zej                  ddddeeddddœ		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd„«       Z	y)ÚVideoGenProviderzäAbstract base class for a video generation backend: implement :attr:`name`
    and :meth:`generate`. ``list_models`` entries are **model families** and may
    add ``speed`` / ``strengths`` / ``price`` / advisory ``modalities``.c                óR   — dgt        t        «      t        t        «      ddddddddœ
S )zàSupported features (keys below, all optional) used for soft validation,
        capability-gated params in the dynamic ``video_generate`` schema, and the
        picker. Default fails closed: text-only, no optional features.Útexté
   é   Fr   )
Ú
modalitiesÚaspect_ratiosÚresolutionsÚmax_durationÚmin_durationÚsupports_audioÚsupports_negative_promptÚsupports_seedÚsupports_upscaleÚmax_reference_images)Úlistr   r   ©Úselfs    ú=/home/ubuntu/.hermes/hermes-agent/agent/video_gen_provider.pyÚcapabilitieszVideoGenProvider.capabilities/   s6   € ð
 "˜(´TÔ:NÓ5OÜÔ 2Ó3ÀRÐYZØ#ÀÐY^Ø %¸qñ	
ð 	
ó    N©	ÚmodelÚ	image_urlÚreference_image_urlsÚdurationÚaspect_ratioÚ
resolutionÚnegative_promptÚaudioÚseedc       	         ó   — y)aD  Generate a video from a prompt, or animate ``image_url`` when given; return
        :func:`success_response` / :func:`error_response`. Unknown ``kwargs`` MUST be
        ignored. Known optional kwarg ``upscale`` (bool): a post-generation high-res
        pass; providers that honor it report ``upscaled: True`` in ``extra``.N© )r&   Úpromptr+   r,   r-   r.   r/   r0   r1   r2   r3   Úkwargss               r'   ÚgeneratezVideoGenProvider.generate:   s   � r)   )ÚreturnúDict[str, Any]©r6   Ústrr+   úOptional[str]r,   r=   r-   zOptional[List[str]]r.   úOptional[int]r/   r<   r0   r<   r1   r=   r2   zOptional[bool]r3   r>   r7   r   r9   r:   )
Ú__name__Ú
__module__Ú__qualname__Ú__doc__r(   ÚabcÚabstractmethodÚDEFAULT_ASPECT_RATIOÚDEFAULT_RESOLUTIONr8   r5   r)   r'   r   r   *   s¿   „ ñLó	
ð 	×Ñà59ÐVZØ48ÐTXØ0ÐDVØ)-ÀtØ"ñ
QØð
QØ%2ð
QØFSð
Qà1ð
QàDQð
Qð ð
Qð ?Bð
Qð 'ð	
Qð 7Eð	
Qð
 ð
Qð
 /2ð
Qð 
ò
Qó ñ
Qr)   r   ÚvideoÚmp4©ÚprefixÚ	extensionc               ó4   — t        j                  d| ||¬«      S )zNDecode base64 video data into ``$HERMES_HOME/cache/videos/``; return the path.ÚvideosrI   )r
   Úsave_b64)Úb64_datarJ   rK   s      r'   Úsave_b64_videorP   H   s   € ä×"Ñ" 8¨X¸fÐPYÔZÐZr)   c               ó4   — t        j                  d| ||¬«      S )z@Write raw video bytes (e.g. an HTTP download body) to the cache.rM   rI   )r
   Ú
save_bytes)ÚrawrJ   rK   s      r'   Úsave_bytes_videorT   M   s   € ä×$Ñ$ X¨s¸6ÈYÔWÐWr)   ÚwebmÚmovÚmkv)z	video/mp4z
video/webmzvideo/quicktimezvideo/x-matroskag     €f@i  €F)rJ   ÚtimeoutÚ	max_bytesÚheadersÚrequire_video_content_typeÚtrusted_originc               óP   — t        j                  d| |||dt        dddd|||¬«      S )a2  Download an (often ephemeral) video URL into ``$HERMES_HOME/cache/videos/``;
    raises on network / HTTP / oversize / empty errors so callers can fall back to the URL.
    ``trusted_origin`` is only for URLs built from the operator's configured provider
    ``base_url`` (see ``provider_media.save_url``).rM   i   )rH   rU   rV   rW   rH   ÚVideoz#Video at {url} was empty (0 bytes).)rJ   rX   rY   Ú
chunk_sizeÚcontent_typesÚurl_extensionsÚdefault_extensionÚlabelÚempty_errorrZ   Úrequire_known_content_typer\   )r
   Úsave_urlÚ_URL_VIDEO_CONTENT_TYPES)ÚurlrJ   rX   rY   rZ   r[   r\   s          r'   Úsave_url_videori   W   s<   € ô ×"Ñ"Ø�#˜f¨gÀØÔ-EØ4ÈØÐ#HØÐ4NØ%ôð r)   r   Ú )Úmodalityr/   r.   Úextrac           	     ó’   — d| |||||rt        |«      nd|dœ}|xs i j                  «       D ]  \  }	}
|j                  |	|
«       Œ |S )zPUniform success dict; ``extra`` keys are added without overriding standard ones.Tr   )ÚsuccessrG   r+   r6   rk   r/   r.   Úprovider)ÚintÚitemsÚ
setdefault)rG   r+   r6   rk   r/   r.   ro   rl   ÚpayloadÚkÚvs              r'   Úsuccess_responserv   o   s_   € ð  %°%À6ÐW_Ø$Á8´#°h´-ÐQRÐ`hñ€Gð ’˜"×#Ñ#Ó%ò !‰ˆˆ1Ø×Ñ˜1˜aÕ ð!à€Nr)   Úprovider_error)Ú
error_typero   r+   r6   r/   c           	     ó   — dd| |||||dœS )z$Build a uniform error response dict.FN)rn   rG   Úerrorrx   r+   r6   r/   ro   r5   ©rz   rx   ro   r+   r6   r/   s         r'   Úerror_responser|   }   s!   € ð  4°%ÀzÐ\aØ¨,ÀHñð r)   c            
      ó¶   — e Zd ZU dZdZded<   dZded<   dZded	<   d
Zded<   dd„Z	dd„Z
dd„Zdd„Zddddeeddddœ		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd„Zy)Ú OpenAICompatibleVideoGenProvideruØ  Generic text/image-to-video over the OpenAI ``client.videos`` API.

    DeepInfra, OpenAI/Sora, and OpenRouter share the ``POST /videos`` async-job
    shape (``create`` â†’ poll â†’ ``download_content``); a concrete backend sets
    ``name``, ``_env_key``, ``_default_base_url`` and ``list_models()`` (entries
    with an ``id`` key; ``default_model()`` uses ``[0]``). Provider-specific
    fields (``image_url``/``negative_prompt``/``seed``) ride in ``extra_body``.
    ÚOPENAI_API_KEYr<   Ú_env_keyzhttps://api.openai.com/v1Ú_default_base_urlg      @ÚfloatÚ_poll_interval_sg      Œ@Ú_poll_deadline_sc                ór   — t         j                  j                  | j                  d«      j	                  «       S )Nrj   )ÚosÚenvironÚgetr€   Ústripr%   s    r'   Ú_api_keyz)OpenAICompatibleVideoGenProvider._api_key›   s$   € Ü�z‰z�~‰~˜dŸm™m¨RÓ0×6Ñ6Ó8Ð8r)   c                ó4   — t        | j                  «       «      S )N)ÚboolrŠ   r%   s    r'   Úis_availablez-OpenAICompatibleVideoGenProvider.is_availablež   s   € Ü�D—M‘M“OÓ$Ð$r)   c                óø  —  |j                   j                  d
i |¤Ž}h d£}t        j                  «       | j                  z   }t        |dd«      |vr©t        j                  «       |k\  r>t        dt        |dd«      › dt        | j                  «      › dt        |dd«      ›d	�«      ‚t        j                  | j                  «       |j                   j                  |j                  «      }t        |dd«      |vrŒ©|S )zCreate the job and poll to a terminal status (any); raise
        :class:`TimeoutError` when ``_poll_deadline_s`` passes first.>   rz   ÚfailedÚcanceledÚ	cancelledÚ	completedÚ	succeededÚstatusNz
video job Úidú?z( did not reach a terminal status within zs (last status=ú)r5   )rM   ÚcreateÚtimeÚ	monotonicr„   ÚgetattrÚTimeoutErrorrp   Úsleeprƒ   Úretriever•   )r&   ÚclientÚcall_kwargsrG   ÚterminalÚdeadlines         r'   Ú_create_and_pollz1OpenAICompatibleVideoGenProvider._create_and_poll¡   sð   € ð %�—‘×$Ñ$Ñ3 {Ñ3ˆÚYˆÜ—>‘>Ó# d×&;Ñ&;Ñ;ˆÜ�e˜X tÓ,°HÑ<Ü�~‰~Ó 8Ò+Ü"Ø ¤¨°°cÓ!:Ð ;ð <%Ü%(¨×)>Ñ)>Ó%?Ð$@ð A$Ü$+¨E°8¸TÓ$BÐ#EÀQðHóð ô
 �J‰J�t×,Ñ,Ô-Ø—M‘M×*Ñ*¨5¯8©8Ó4ˆEô �e˜X tÓ,°HÒ<ð ˆr)   c                ó°   — t         j                  j                  | j                  j	                  «       › d�d«      j                  «       xs | j                  S )NÚ	_BASE_URLrj   )r†   r‡   rˆ   ÚnameÚupperr‰   r�   r%   s    r'   Ú	_base_urlz*OpenAICompatibleVideoGenProvider._base_url²   s>   € Ü�z‰z�~‰~ §¡§¡Ó!2Ð 3°9Ð=¸rÓB×HÑHÓJÒdÈd×NdÑNdÐdr)   Nr*   c       	   	     óê  ‡ ‡‡‡— ‰r‰j                  «       st        dd‰ j                  ¬«      S ‰ j                  «       s%t        ‰ j                  › d�d‰ j                  ¬«      S 	 dd l}|xs ‰ j                  «       Š‰s&t        d	‰ j                  › d
�d‰ j                  ¬«      S d*ˆˆˆˆ fd„}|‰||
dœj                  «       D ��ci c]
  \  }}|�||“Œ }}}‰‰dœ}|rt        |«      |d<   |r||d<   |r||d<   ddl
m} ‰ j                  «       ‰ j                  «       dœ} ||d   «      }|�||d<    |j                  d+i |¤Ž}	 	 ‰ j                  ||«      }t%        |dd «      }|dvrFt%        |dd «      } ||rt        |«      nd|›�d «      t%        |dd «      }t'        |«      r |«        S S d }t%        |d!d «      xs g D ]8  }t)        |t*        «      r|j-                  d"«      nt%        |d"d «      xs d }|sŒ8 n 	 |r!t        t/        |‰ j                  ¬#«      «      }nS|j0                  j3                  |j4                  «      j7                  «       }t        t9        |‰ j                  ¬#«      «      }t;        |‰‰|rd'nd(‰|xs d‰ j                  ¬)«      t%        |dd «      }t'        |«      r |«        S S # t        $ r t        dd‰ j                  ¬«      cY S w xY wc c}}w # t        $ rd}t         j#                  d‰ j                  d¬«        |‰ j                  › d|› �d«      cY d }~t%        |dd «      }t'        |«      r |«        S S d }~ww xY w# t        $ rm}|s= |‰ j                  › d$|› �d%«      cY d }~t%        |dd «      }t'        |«      r |«        S S t         j#                  d&‰ j                  |«       |}Y d }~�ŒRd }~ww xY w# t%        |dd «      }t'        |«      r |«        w w xY w),Nzprompt is requiredÚinvalid_request)rz   rx   ro   z is not setÚmissing_credentialsr   z8openai Python package not installed (pip install openai)Úmissing_dependencyzno z, video model available (live catalog empty?)Úno_modelrz   c                ó:   •— t        | |‰j                  ‰‰‰¬«      S )Nr{   )r|   r¦   )rz   rx   r/   Úmodel_idr6   r&   s     €€€€r'   Úfailz7OpenAICompatibleVideoGenProvider.generate.<locals>.failÑ   s#   ø€ Ü!Ø¨
¸T¿Y¹YÈhÐ_eØ)ôð r)   )r1   r/   r,   r3   )r+   r6   ÚsecondsÚsizeÚ
extra_body)Úbuild_keepalive_http_client)Úapi_keyÚbase_urlr¶   Úhttp_clientz%s video generation failedT)Úexc_infoz video generation failed: Ú	api_errorÚcloser”   )r’   r“   zvideo job ended with status=Ú
job_failedÚdatarh   )rJ   z7 video job succeeded but no output could be retrieved: Úempty_responsez3%s: saving video locally failed (%s); returning URLÚimager   )rG   r+   r6   rk   r/   r.   ro   )rz   r<   rx   r<   r9   r:   r5   )r‰   r|   r¦   rŠ   r€   ÚopenaiÚImportErrorÚdefault_modelrq   r<   Úagent.process_bootstrapr´   r¨   ÚOpenAIr£   Ú	ExceptionÚloggerÚdebugr›   ÚcallableÚ
isinstanceÚdictrˆ   ri   rM   Údownload_contentr•   ÚreadrT   rv   ) r&   r6   r+   r,   r-   r.   r/   r0   r1   r2   r3   r7   r¿   r°   rt   ru   r³   r    r´   Úclient_kwargsr·   rŸ   rG   Úexcrº   r”   Ú	job_errorrh   ÚitemÚ	video_refrS   r¯   s    ``    `                        @r'   r8   z)OpenAICompatibleVideoGenProvider.generateµ   s  û€ ñ ˜VŸ\™\œ^Ü!Ð(<ÐIZÐei×enÑenÔoÐoØ�}‰}ŒÜ!ØŸ™˜ {Ð3Ð@UÐ`d×`iÑ`iôð ð	Ûð Ò0˜D×.Ñ.Ó0ˆÙÜ!Ø˜DŸI™I˜;Ð&RÐSØ%°·	±	ôð ÷
	ð 	ð $3ÀLØ&Øñ÷ ‰e‹g÷
á��1ð
 ˆ}ð ˆq‰Dð
ˆ
ñ 
ð 19ÀFÑ&KˆÙÜ%(¨£]ˆK˜	Ñ"ÙØ",ˆK˜ÑÙØ(2ˆK˜Ñ%õ 	Hà48·M±M³OÐQU×Q_ÑQ_ÓQaÑ(bˆÙ1°-À
Ñ2KÓLˆØÐ"Ø+6ˆM˜-Ñ(Ø�—‘Ñ/ Ñ/ˆð+	ðXØ×-Ñ-¨f°kÓB�ô ˜U H¨dÓ3ˆFØÐ7Ñ7ä# E¨7°DÓ9�	Ù©iœC 	œNÐ?[Ð\bÐ[eÐ=fÐhtÓuô: ˜F G¨TÓ2ˆEÜ˜ŒÙ•ð ð3 ˆCÜ  v¨tÓ4Ò:¸ò �Ü*4°T¼4Ô*@�t—x‘x ”ÄgÈdÐTYÐ[_ÓF`ÒiÐei�ÚÙðð

 ÙÜ #¤N°3¸t¿y¹yÔ$IÓ J‘Ià Ÿ-™-×8Ñ8¸¿¹ÓB×GÑGÓI�CÜ #Ô$4°SÀÇÁÔ$KÓ L�Iô $Ø x¸Ù$-™°6ÈØ!š Q°·±ôô ˜F G¨TÓ2ˆEÜ˜ŒÙ•ð øôq ò 	Ü!ØPØ/¸$¿)¹)ôò ð	üó(
øô: ò XÜ—‘Ð9¸4¿9¹9Èt�ÔTÙ˜tŸy™y˜kÐ)CÀCÀ5ÐIÈ;ÓWÔWôH ˜F G¨TÓ2ˆEÜ˜ŒÙ•ð ûðOXûô4 ò  ÙÙ 4§9¡9 +Ð-dÐehÐdiÐ jÐl|Ó}Ô}ô ˜F G¨TÓ2ˆEÜ˜ŒÙ•ð ô —‘ÐRÐTX×T]ÑT]Ð_bÔcØ–	ûð	 ûô ˜F G¨TÓ2ˆEÜ˜ŒÙ•ð ús˜   Á%J: ÃK!ÅK' Å6O Æ1A
O Ç<O È A6M É6#O Ê:!KËKË'	MË09MÌ)MÌ*O ÍMÍO Í	OÍ OÍ9OÍ:O Î#OÏO ÏOÏO Ï"O2)r9   r<   )r9   rŒ   )rŸ   r   r    r:   r9   r   r;   )r?   r@   rA   rB   r€   Ú__annotations__r�   rƒ   r„   rŠ   r�   r£   r¨   rE   rF   r8   r5   r)   r'   r~   r~   ˆ   sé   … ñð %€HˆcÓ$Ø8Ð�sÓ8ð
 "Ð�eÓ!Ø#Ð�eÓ#ó9ó%óó"eð 6:ÐVZØ48ÐTXØ0ÐDVØ)-ÀtØ"ñhØðhØ%2ðhØFSðhà1ðhàDQðhð ðhð ?Bðhð 'ð	hð 7Eð	hð
 ðhð
 /2ðhð 
ôhr)   r~   )rO   r<   rJ   r<   rK   r<   r9   r   )rS   ÚbytesrJ   r<   rK   r<   r9   r   )rh   r<   rJ   r<   rX   r‚   rY   rp   rZ   zOptional[Dict[str, str]]r[   rŒ   r\   rŒ   r9   r   )rG   r<   r+   r<   r6   r<   rk   r<   r/   r<   r.   rp   ro   r<   rl   zOptional[Dict[str, Any]]r9   r:   )rz   r<   rx   r<   ro   r<   r+   r<   r6   r<   r/   r<   r9   r:   )&rB   Ú
__future__r   rC   Úloggingr†   r™   Úpathlibr   Útypingr   r   r   r   r	   Úagentr
   Úagent.provider_baser   Ú	getLoggerr?   rÅ   r   rÑ   rE   r   rF   r   rP   rT   rg   ri   rv   r|   r~   Úbase64ÚdatetimeÚuuidr5   r)   r'   ú<module>rÝ      sÎ  ðòõ #ã 
Û Û 	Û Ý ß 3Õ 3å  Ý 3à	ˆ×	Ñ	˜8Ó	$€ð
)Ð �oó ð Ð à&OÐ �OÓ OØÐ ôQÐ*ô Qð< 18Èõ [ð
 07Àuõ Xð  fÀÐ\añÐ ð ØØ&Ø(,Ø',Ø ñØ	ðð ðð ð	ð
 ðð &ðð !%ðð ðð 
óð2 =CÐXZØÈñØðØðØ'*ðØ69ðØRUðàðà!$ðà-Eðð óð &6ÀrÐXZØ¨"ñØðØ"ðØADðØRUðàðà$'ðð óôUÐ'7ô Uóx Û Ü r)   