Ë
    âz¦i£  ã                  ó®   — 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m	Z	 ddl
mZmZmZ  ej                  e«      Ze G d„ d«      «       Z G d„ d	«      Zy)
a  VLM client for vision-language scene analysis.

Sends a JPEG frame plus a text prompt to the OpenAI-compatible vision endpoint
(modelapi.klass.dev) and returns the model's text response.  Follows the same
synchronous ``requests``-with-urllib-fallback pattern as ``LLMClient._post``.
é    )ÚannotationsN)Ú	dataclassÚfield)ÚAnyÚDictÚOptionalc                  ó€   — e Zd ZU dZ ed„ ¬«      Zded<    ed„ ¬«      Zded<    ed¬	«      Zd
ed<   dZ	ded<   dd„Z
y)Ú	VLMConfiga|  Configuration for the VLM client.

    Attributes:
        endpoint: Full URL for the chat completions endpoint.
        model: Vision model identifier (e.g. the Qwen vision variant on modelapi).
        api_key: Bearer token for Authorization header.  Reads from
            ``MODELAPI_KEY`` env var if not supplied explicitly.
        timeout_s: Request timeout in seconds.
    c                 ó.   — t        j                  dd«      S )NÚVLM_ENDPOINTz.https://modelapi.klass.dev/v1/chat/completions©ÚosÚgetenv© ó    úG/home/nelsen/Projects/kognitive/orchestrator/src/services/vlm_client.pyú<lambda>zVLMConfig.<lambda>!   s   € ¤§	¡	ØØ<ó!
€ r   )Údefault_factoryÚstrÚendpointc                 ó.   — t        j                  dd«      S )NÚ	VLM_MODELzQwen3.5-122B-A10Br   r   r   r   r   zVLMConfig.<lambda>'   s   € ¤§	¡	¨+Ð7JÓ K€ r   ÚmodelN)ÚdefaultzOptional[str]Úapi_keyé   ÚintÚ	timeout_sc                ó®   — | j                   €It        j                  d«      xs, t        j                  d«      xs t        j                  d«      | _         y y )NÚMODELAPI_KEYÚMODEL_API_KEYÚOPENAI_API_KEY)r   r   r   )Úselfs    r   Ú__post_init__zVLMConfig.__post_init__,   sG   € Ø<‰<Ðä—	‘	˜.Ó)ò /Ü—9‘9˜_Ó-ò/ä—9‘9Ð-Ó.ð Lð  r   )ÚreturnÚNone)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   Ú__annotations__r   r   r   r$   r   r   r   r
   r
      sU   … ññ ñ
ô€Hˆcó ñ ÙKô€Eˆ3ó ñ #¨4Ô0€Gˆ]Ó0Ø€IˆsÓôr   r
   c                  ó*   — e Zd ZdZddd„Zdd„Zd	d„Zy)
Ú	VLMClienta&  Synchronous HTTP client for the vision-language model endpoint.

    Usage::

        client = VLMClient()
        analysis = client.analyze_scene(jpeg_bytes, "Describe the scene.")
        # "There are two unconscious workers near a chemical container..."

    The endpoint accepts an OpenAI-compatible chat completion request with an
    image embedded as a base64 data URL.  This is a *synchronous* method so
    it can be offloaded to a thread pool from async code via
    ``loop.run_in_executor(None, client.analyze_scene, frame, prompt)``.
    Nc                ó*   — |xs
 t        «       | _        y ©N)r
   Úconfig)r#   r0   s     r   Ú__init__zVLMClient.__init__D   s   € ØÒ+¤	£ˆr   c                ó†  — |st         j                  d«       y|st         j                  d«       yt        j                  |«      j	                  d«      }| j
                  j                  dddd|› id	œd
|dœgdœgdddddœ}	 | j                  |«      }|d   d   d   d   }t        |t        «      rt         j                  d|dd «       |S t         j                  d|«       y# t        t        t        f$ r }t         j                  d|«       Y d}~yd}~wt        $ r }t         j                  d|«       Y d}~yd}~ww xY w)a3  Send a JPEG frame and a text prompt to the VLM; return the reply.

        Args:
            jpeg_bytes: Raw JPEG bytes of the frame to analyse.
            prompt: Natural-language instruction for the model.

        Returns:
            The model's text response, or an empty string on any error.
        u<   VLMClient.analyze_scene called with empty bytes â€” skippingÚ u=   VLMClient.analyze_scene called with empty prompt â€” skippingÚasciiÚuserÚ	image_urlÚurlzdata:image/jpeg;base64,)Útyper6   Útext)r8   r9   )ÚroleÚcontentgffffffæ?i   gš™™™™™é?é   )r   ÚmessagesÚtemperatureÚ
max_tokensÚtop_pÚtop_kÚchoicesr   Úmessager;   u   VLM response: %sâ€¦Néx   z(VLM response content is not a string: %rz"Unexpected VLM response format: %szVLM analyze_scene failed: %s)ÚloggerÚwarningÚbase64Ú	b64encodeÚdecoder0   r   Ú_postÚ
isinstancer   ÚinfoÚerrorÚKeyErrorÚ
IndexErrorÚ	TypeErrorÚ	Exception)r#   Ú
jpeg_bytesÚpromptÚb64ÚpayloadÚresponser;   Úexcs           r   Úanalyze_scenezVLMClient.analyze_sceneG   sG  € ñ ÜN‰NÐYÔZØáÜN‰NÐZÔ[Øä×Ñ˜zÓ*×1Ñ1°'Ó:ˆð —[‘[×&Ñ&ð #ð %0Ø*/Ð3JÈ3È%Ð1PÐ)Qñð "(°Ñ8ð ñ	ðð ØØØñ##
ˆð(	Ø—z‘z 'Ó*ˆHØ˜yÑ)¨!Ñ,¨YÑ7¸	ÑBˆGÜ˜'¤3Ô'Ü—‘Ð1°7¸4¸C°=ÔAØÜL‰LÐCÀWÔMØøÜœ*¤iÐ0ò 	ÜL‰LÐ=¸sÔCÜûÜò 	ÜL‰LÐ7¸Ô=Üûð	ús+   ÂA	C% ÃC% Ã%E Ã9DÄE Ä D;Ä;E c                ó.  — ddi}| j                   j                  rd| j                   j                  › |d<   t        j                  |«      j	                  d«      }	 ddl}|j                  | j                   j                  ||| j                   j                  ¬«      }|j                  «        |j                  «       S # t        $ r Y nt        $ r}t        d	|› «      |‚d}~ww xY wddl}ddl}|j                  j!                  | j                   j                  ||d
¬«      }	 |j                  j#                  || j                   j                  ¬«      5 }	|	j%                  «       j'                  d«      }
t        j(                  |
«      cddd«       S # 1 sw Y   yxY w# |j*                  j,                  $ rB}|j%                  «       j'                  dd¬«      }t        d|j.                  › d|› «      |‚d}~wt        $ r}t        d	|› «      |‚d}~ww xY w)a  POST JSON payload to the VLM endpoint.

        Tries ``requests`` first (faster, richer error messages); falls back to
        stdlib ``urllib`` so the client works without the optional dependency.

        Args:
            payload: Dict to serialise as JSON request body.

        Returns:
            Parsed JSON response dict.

        Raises:
            RuntimeError: On any HTTP or network error.
        zContent-Typezapplication/jsonzBearer ÚAuthorizationzutf-8r   N)ÚjsonÚheadersÚtimeoutzVLM request failed: ÚPOST)Údatar\   Úmethod)r]   Úreplace)ÚerrorszVLM HTTP error z: )r0   r   r[   ÚdumpsÚencodeÚrequestsÚpostr   r   Úraise_for_statusÚImportErrorrQ   ÚRuntimeErrorÚurllib.errorÚurllib.requestÚrequestÚRequestÚurlopenÚreadrI   ÚloadsrM   Ú	HTTPErrorÚcode)r#   rU   r\   r_   re   rV   rW   Úurllibrl   ÚhandleÚbodyÚdetails               r   rJ   zVLMClient._post‚   sæ  € ð "Ð#5Ð6ˆØ;‰;×ÒØ)0°·±×1DÑ1DÐ0EÐ'FˆGOÑ$äz‰z˜'Ó"×)Ñ)¨'Ó2ˆð	FÛà—}‘}Ø—‘×$Ñ$ØØØŸ™×-Ñ-ð	 %ó ˆHð ×%Ñ%Ô'Ø—=‘=“?Ð"øÜò 	ÙÜò 	FÜÐ!5°c°UÐ;Ó<À#ÐEûð	Fúó 	Ûà—.‘.×(Ñ(ØK‰K× Ñ ØØØð	 )ó 
ˆð	FØ—‘×'Ñ'¨¸¿¹×9NÑ9NÐ'ÓOð (ÐSYØ—{‘{“}×+Ñ+¨GÓ4Ü—z‘z $Ó'÷(÷ (ò (ûð |‰|×%Ñ%ò 	PØ—X‘X“Z×&Ñ& w°yÐ&ÓAˆFÜ °·±°
¸"¸V¸HÐEÓFÈCÐOûÜò 	FÜÐ!5°c°UÐ;Ó<À#ÐEûð	Fúsa   ÁA B= Â=	C$ÃC$ÃCÃC$Ä#1F Å4FÆ	F ÆFÆF ÆF ÆHÆ7=G4Ç4HÈ HÈHr/   )r0   zOptional[VLMConfig]r%   r&   )rR   ÚbytesrS   r   r%   r   )rU   úDict[str, Any]r%   rx   )r'   r(   r)   r*   r1   rX   rJ   r   r   r   r-   r-   5   s   „ ñô,ó5ôv7Fr   r-   )r*   Ú
__future__r   rG   r[   Úloggingr   Údataclassesr   r   Útypingr   r   r   Ú	getLoggerr'   rE   r
   r-   r   r   r   ú<module>r~      s^   ðñõ #ã Û Û Û 	ß (ß &Ñ &à	ˆ×	Ñ	˜8Ó	$€ð ÷ð ó ð÷@DFò DFr   