
    epj70                        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	m
Z
 dZdZd&d	Zd'd(dZd)dZd*dZd+dZd,dZd-dZd+dZd.dZd/dZd0d Zd!dd"d1d%ZdS )2z0Shared helpers for direct xAI HTTP integrations.    )annotationsN)AnyDictOptionali ' i  returnboolc                    	 ddl m}   | dd          pd                                rdS nE# t          $ r8 t          j                            dd                                          rY dS Y nw xY w	 ddlm}  |            dz  }|	                                sdS t          j        |                    d	
                    }t          |t                    r|                    d          nd}t          |t                    r|                    d          nd}t          |t                    r|                    d          nd}t          |t                    r|                    d          nd}t          |pd                                          rdS t          |t                    r|                    d          nd}t          |t                    r|                    d          nd}	t          |	t                     rU|	D ]R}
t          |
t                    st          |
                    dd          pd                                          r dS SdS # t"          $ r Y dS w xY w)u  Cheap probe — return True when xAI credentials are *likely* usable.

    Deliberately avoids :func:`resolve_xai_http_credentials` so callers in
    hot-paint paths (``hermes tools`` repaint, tool-registration scans,
    ``WebSearchProvider.is_available()``) don't incur disk locks or — in
    the OAuth path — a network token refresh. The ABC contract on
    :meth:`agent.web_search_provider.WebSearchProvider.is_available`
    explicitly forbids network calls for exactly this reason.

    Resolution order, fast-to-slow:

    1. ``XAI_API_KEY`` env var (cheapest; covers explicit-key users).
    2. ``~/.hermes/auth.json`` has a non-empty ``providers.xai-oauth.tokens.access_token``
       (single file read, no expiry check, no refresh).
    3. ``credential_pool.xai-oauth`` has any entry with a non-empty
       ``access_token`` (covers multi-account ``hermes auth add xai-oauth``
       grants that are pool-only / ``manual:device_code``).

    Returns False on any exception so a corrupted auth store can't block
    other availability scans. Truthful refresh + expiry handling happens
    in ``search()`` (or whichever caller actually makes the request).
    r   )
get_secretXAI_API_KEY Tget_hermes_homez	auth.jsonFutf-8encoding	providersN	xai-oauthtokensaccess_tokencredential_pool)agent.secret_scoper
   stripImportErrorosenvirongethermes_constantsr   existsjsonloads	read_text
isinstancedictstrlist	Exception)r
   r   	auth_pathstorer   	xai_stater   r   r   entriesentrys              4/home/thesage/.hermes/hermes-agent/tools/xai_http.pyhas_xai_credentialsr-      s   .111111
 J}b))/R6688 	4		    :>>-,,2244 	44	 	444444#O%%3	!! 	5
9...@@AA.8.E.EOEIIk***4	2<Y2M2MWIMM+...SW	,6y$,G,GQx(((T5?5M5MWvzz.111SW|!r""((** 	4 ;EUD:Q:Q[%))$5666W[ /400O,,, 	
 gt$$ 	      !%.. uyy44:;;AACC  44 u   uus5   + <A-,A-1'I C;I B?I I 
I)(I)namer$   c                    	 ddl m} n0# t          $ r# t          j                            | |          cY S w xY w ||           }||n|S )zRead ``name`` from ``~/.hermes/.env`` first, then ``os.environ``.

    Wraps :func:`hermes_cli.config.get_env_value` so tests can patch
    ``tools.xai_http.get_env_value`` to inject dotenv-only secrets into the
    xAI credential resolver.
    r   )get_env_value)hermes_cli.configr0   r   r   r   r   )r.   default_hermes_get_env_valuevalues       r,   r0   r0   O   st    -LLLLLLL - - -z~~dG,,,,,- "!$''E%5572s   	 *66c                 @    	 ddl m}  n# t          $ r d} Y nw xY wd|  S )z>Return a stable Hermes-specific User-Agent for xAI HTTP calls.r   __version__unknownzHermes-Agent/)
hermes_clir7   r&   r6   s    r,   hermes_xai_user_agentr:   _   sN     *******       (;(((s   	 Dict[str, str]c                 "    dt                      iS )u5  Default headers for OpenAI-SDK and raw HTTP clients talking to xAI.

    Replaces the OpenAI Python SDK's identifying ``User-Agent: OpenAI/Python …``
    so chat/completions and Responses traffic is attributed as Hermes Agent,
    matching the direct HTTP integrations (search, TTS, STT, image, video).
    z
User-Agent)r:        r,   hermes_xai_default_headersr?   h   s     /1122r>   section_nameDict[str, Any]c                    	 ddl m}  |            }t          |t                    r|                    |           nd}t          |t                    r|ni S # t
          $ r i cY S w xY w)z=Return a top-level Hermes config section as a dict, or empty.r   )load_configN)r1   rC   r"   r#   r   r&   )r@   rC   cfgsections       r,   _load_config_sectionrF   r   s    111111kmm+5c4+@+@J#'','''d$Wd33;ww;   			s   AA A&%A&r4   r   r2   c                    t          | t                    r| S t          | t                    r2|                                                                 }|dv rdS |dv rdS |S )N>   1onyestrueenabledT>   0noofffalsedisabledF)r"   r   r$   r   lower)r4   r2   
normalizeds      r,   _coerce_boolrT   ~   sk    % % [[]]((**
>>>4@@@5Nr>   Optional[int]c                   | dS t          | t                    rZ|                                                                 }|dv rdS |dv rdS 	 t	          |          } n# t
          $ r
 t          cY S w xY wt          | t          t          f          r,t	          |           }|dk    rdS t          |t                    S t          S )zNormalize an xAI storage TTL.

    Returns:
        int seconds for an expiring file,
        None for permanent storage (omit expires_after on the wire).
    N>   r   r2   >   rM   nonenullneverforever	permanentr   )
r"   r$   r   rR   int
ValueError&SAFE_XAI_STORAGE_EXPIRES_AFTER_SECONDSfloatmin%MAX_XAI_STORAGE_EXPIRES_AFTER_SECONDS)r4   rS   secondss      r,   _coerce_expires_afterrc      s     }t% 	:[[]]((**
((4OOO4	:
OOEE 	: 	: 	:9999	:%#u&& Ce**a<<47ABBB11s   A A10A1c                   t          |           }t          |t                    r|                    d          nd}t          |t                    r|                    d          nd}t          |t                    r|ni }t	          |                    d          d          }t	          |                    d          d          }t          |                    d                    }|||dS )	a  Read storage settings for xAI Imagine under image_gen/video_gen config.

    Supported config shape:

        image_gen:
          xai:
            storage:
              enabled: true
              public_url: true
              expires_after: null     # omit for permanent public URLs

    The same shape is accepted under ``video_gen.xai.storage``. Storage is on
    by default so xAI returns permanent public URLs instead of short-lived CDN URLs.
    xaiNstoragerL   T
public_urlexpires_after)rL   rg   rh   )rF   r"   r#   r   rT   rc   )r@   rE   xai_sectionrf   rL   rg   rh   s          r,   read_xai_imagine_storage_configrj      s     #<00G(27D(A(AK'++e$$$tK,6{D,I,ISkooi(((tG#GT22:ggG7;;y11488Ggkk,77>>J)'++o*F*FGGM  &  r>   filename_prefix	extensionOptional[Dict[str, Any]]c                  t          |           }|d         sdS t          j                            t          j                  }|                    d          }t          j                    j        dd         }|                    d          pd}| d| d| d| t          |d                   d	}|d
         |d
         |d
<   |S )zAReturn an xAI ``storage_options`` payload, or None when disabled.rL   Nz%Y%m%d-%H%M%S   .bin-rg   )filenamerg   rh   )
rj   datetimenowUTCstrftimeuuiduuid4hexlstripr   )	r@   rk   rl   rD   ru   tsshortextpayloads	            r,   build_xai_storage_optionsr      s     *,
7
7Cy> t




-
-C	o	&	&BJLLRaR E


3


(5C&;;;;e;;c;;3|,-- G ?'#&#7 Nr>   c                    t          |           }|d         sdS |d         d}n|d         dz  }d|dd	|d
k    rdnd }d| d|  dS )z5User-facing notice for first xAI Imagine storage use.rL   r   rh   Nzwithout an automatic expiryiQ z
for about gz day   szMxAI Imagine storage is enabled so generated media gets a reusable public URL zK. xAI may bill for stored files and public URL hosting. Disable this with `zL.xai.storage.enabled: false` or set `expires_after` to change the retention.)rj   )r@   rD   	retentiondayss       r,   xai_storage_notice_textr      s    
),
7
7Cy> r
?#1		?#|4GGGGDAIISS2GG		:	: 	:'3	: 	: 	:r>   Optional[str]c                   t          |           }|sdS 	 ddlm}  |            dz  }|                    dd           ||  dz  }|                                rdS |                    t          j                            t          j                  	                                dz   d	
           |S # t          $ r |cY S w xY w)zBReturn the storage notice once per Hermes home, then mark it seen.Nr   r   stateT)parentsexist_ok_xai_storage_notice_seen
r   r   )r   r   r   mkdirr   
write_textrt   ru   rv   	isoformatr&   )r@   noticer   
marker_dirmarkers        r,   "maybe_mark_xai_storage_notice_seenr      s    $\22F t444444$_&&0
555GGGG==?? 	4(+//==GGIIDP[bccc   s   AB2 AB2 2C CF)force_refreshapi_key_hintr   r   c                <   	 ddl m} ddlm}  |d          }| r|                    |          n|                                }| r||                                }t          t          |dd          pt          |dd                                                    }t          t          |dd          pt          |d	d          p|j	                                                  
                    d
          }t          t          d          pt          d          pd                                          
                    d
          }|                    ||          }	|rd||	dS n# t          $ r Y nw xY w	 ddlm}
  |
ddt                    }n@# t           $ r3 t          t          d          pd                                          }Y nw xY wt          t          d          pd                                          
                    d
          }	d||	dS )u;  Resolve bearer credentials for direct xAI HTTP endpoints.

    Prefers Hermes-managed xAI OAuth credentials when available, then falls back
    to ``XAI_API_KEY`` resolved via ``hermes_cli.config.get_env_value`` so keys
    stored in ``~/.hermes/.env`` (the standard Hermes location) are honored —
    not just ones already exported into ``os.environ``. This keeps direct xAI
    endpoints (images, TTS, STT, etc.) aligned with the main runtime auth model
    and preserves the regression contract from PR #17140 / #17163.

    Set ``force_refresh=True`` to perform an unconditional OAuth refresh.
    Reactive callers should also pass the rejected bearer as ``api_key_hint``
    so a freshly loaded multi-account pool refreshes the exact issuing entry,
    not whichever entry its strategy would otherwise select first.
    r   )	load_poolNr   runtime_api_keyr   r   runtime_base_urlbase_url/HERMES_XAI_BASE_URLXAI_BASE_URL)fallback)providerapi_keyr   )resolve_provider_secretr   re   )
env_getterzhttps://api.x.ai/v1)agent.credential_poolr   hermes_cli.authauthtry_refresh_matchingselectr$   getattrr   DEFAULT_XAI_OAUTH_BASE_URLrstripr0    _xai_validate_inference_base_urlr&   tools.tool_backend_helpersr   r   )r   r   r   auth_modpoolr+   r   fallback_base_urloverride_base_urlr   r   r   s               r,   resolve_xai_http_credentialsr     s   &(333333******y%% D%%l333 	
  	"U] KKMMEE,d33 2unb11
 
 %'' 	  E-t44 3uj"--32
 
 %''&&++	 	
  /00 ^,,
 
 %''&&++	 	
 <<& = 
 
  	''$  	    BFFFFFF))-=YYY B B BmM228b99??AAB=00I4IJJPPRRYYZ]^^H  s$   E&E* *
E76E7;F :GG)r   r   )N)r.   r$   )r   r$   )r   r;   )r@   r$   r   rA   )r4   r   r2   r   r   r   )r4   r   r   rU   )r@   r$   rk   r$   rl   r$   r   rm   )r@   r$   r   r$   )r@   r$   r   r   )r   r   r   r   r   r;   )__doc__
__future__r   rt   r   r   rx   typingr   r   r   ra   r^   r-   r0   r:   r?   rF   rT   rc   rj   r   r   r   r   r=   r>   r,   <module>r      s   6 6 " " " " " "   				  & & & & & & & & & & ): %)9 &< < < <~3 3 3 3 3 ) ) ) )3 3 3 3	 	 	 		 	 	 	2 2 2 26   >   0   $   *  "&H H H H H H H Hr>   