a
    ™iÕj”  ã                   @   st   d Z ddlZddlZddlZddlZddlmZ ej dd¡Zddd„Z	e	ƒ  ej d¡Z
d	d
„ ZG dd„ dƒZdS )uJ  Client for calling an LLM directly via Groq, bypassing both the EXL AI Gateway (blocked by
its own WAF from this AWS account's egress IP, confirmed by direct testing â€” not an assumption)
and AWS Bedrock (Anthropic models aren't enabled in this account without a separate use-case
form that only an account admin can submit).

Groq exposes a native OpenAI-compatible endpoint directly (api.groq.com/openai/v1), so this
uses the OpenAI SDK pointed at it â€” the exact same pattern the original gateway client used,
just a different base_url. litellm was tried first as a multi-provider translation layer, but
it produced an intermittent, unreproducible "model_not_found" error for an unrelated,
already-deprecated model name mid-request â€” calling Groq's own endpoint directly removes that
extra routing layer and its failure mode entirely.
é    N)ÚOpenAIÚMODEL_IDzopenai/gpt-oss-120bc                 C   s‚   | pt j t j t¡d¡} t j | ¡s*dS t| ƒD ]J}| ¡ }|r2| d¡s2d|vrVq2| 	d¡\}}}t j
 | ¡ | ¡ ¡ q2dS )zJMinimal .env loader so this runs without an extra dependency for one file.z.envNú#ú=)ÚosÚpathÚjoinÚdirnameÚ__file__ÚexistsÚopenÚstripÚ
startswithÚ	partitionÚenvironÚ
setdefault)r   ÚlineÚkeyÚ_Úvalue© r   ú*/home/ec2-user/esure-agent/model_client.pyÚload_env   s    r   ÚGROQ_API_KEYc                 C   sJ   t  d| ¡}|sdS t| d¡ƒ| d¡ }}|dkr>|d n|}|d S )u�   Groq's 429 body says e.g. 'Please try again in 892.5ms' or '...in 1.2s' â€” use that
    exact wait instead of a guessed backoff.ztry again in ([\d.]+)(ms|s)\bNé   é   Úmsiè  gš™™™™™É?)ÚreÚsearchÚfloatÚgroup)Z
error_textÚmatchr   ZunitÚsecondsr   r   r   Ú_parse_retry_after)   s    r#   c                   @   s(   e Zd ZdZeefdd„Zddd„ZdS )ÚGatewayClientuO  Named GatewayClient for drop-in compatibility with agent_core.py's existing calls â€”
    it no longer talks to the EXL gateway, but the request/response shape agent_core.py
    depends on (message.content, message.tool_calls[].function.{name,arguments}) is
    unchanged, so nothing else needed to change when this swap happened.
    c                 C   s$   |st dƒ‚|| _td|d�| _d S )Nz#GROQ_API_KEY must be set (see .env)zhttps://api.groq.com/openai/v1)Zbase_urlÚapi_key)ÚRuntimeErrorÚmodelr   Ú_client)Úselfr'   r%   r   r   r   Ú__init__;   s    zGatewayClient.__init__Nc           
      K   sÄ   t |ƒ}|r ||d< |r ||d< | dd¡ d }tdƒD ]‚}z0| jjjjf | j|dœ|¤Ž}|jd j	W   S  t
jy¸ } z2|}tt|ƒƒp˜d|d  }	t |	¡ W Y d }~q8d }~0 0 q8|‚d S )	NÚtoolsÚtool_choiceZtemperaturer   é   )r'   Úmessagesr   r   )Údictr   Úranger(   ZchatZcompletionsZcreater'   ÚchoicesÚmessageÚopenaiZRateLimitErrorr#   ÚstrÚtimeÚsleep)
r)   r.   r+   r,   ÚkwargsÚlast_excZattemptÚresponseÚexcZwait_sr   r   r   ÚcompleteA   s(    ÿÿ"zGatewayClient.complete)NN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r*   r;   r   r   r   r   r$   4   s   r$   )N)r?   r   r   r5   r3   r   r   Úgetr   r   r   r#   r$   r   r   r   r   Ú<module>   s   
