o
    ưi3                     @   s   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 d dlZd dl	m
Z
 d dlmZ ddlmZmZmZmZmZ dZd	Zd
ZdZG dd dZdS )    N)datetime)AnyDictOptional)verbose_logger)_get_httpx_client   )APIKeyExpiredErrorGetAccessTokenErrorGetAPIKeyErrorGetDeviceCodeErrorRefreshAPIKeyErrorzIv1.b507a08c87ecfe98z$https://github.com/login/device/codez+https://github.com/login/oauth/access_tokenz0https://api.github.com/copilot_internal/v2/tokenc                   @   s   e Zd ZdddZdefddZdefddZdee fd	d
Zde	ee
f fddZdddZddee de	eef fddZde	eef fddZdedefddZdefddZdS )AuthenticatorreturnNc                 C   sV   t dt jd| _t j| jt dd| _t j| jt dd| _|   dS )zJInitialize the GitHub Copilot authenticator with configurable token paths.ZGITHUB_COPILOT_TOKEN_DIRz ~/.config/litellm/github_copilotZ GITHUB_COPILOT_ACCESS_TOKEN_FILEzaccess-tokenZGITHUB_COPILOT_API_KEY_FILEzapi-key.jsonN)	osgetenvpath
expanduser	token_dirjoinaccess_token_fileapi_key_file_ensure_token_dirself r   `/home/app/Keep/.python/lib/python3.10/site-packages/litellm/llms/github_copilot/authenticator.py__init__   s   

zAuthenticator.__init__c                 C   sT  z*t | jd}|  }|r|W  d   W S W d   n1 s$w   Y  W n ty8   td Y nw tdD ]f}td|d  d z5| 	 }zt | jd}|
| W d   n1 sfw   Y  W n tyz   td	 Y nw |W   S  tttfy } ztd
|d  dt|  W Y d}~q=d}~ww tddd)z
        Login to Copilot with retry 3 times.

        Returns:
            str: The GitHub access token.

        Raises:
            GetAccessTokenError: If unable to obtain an access token after retries.
        rNz4No existing access token found or error reading file   z!Access token acquisition attempt r   z/3wz!Error saving access token to filezFailed attempt z: z+Failed to get access token after 3 attempts  messagestatus_code)openr   readstripIOErrorr   warningrangedebug_loginwriteerrorr   r
   r   str)r   faccess_tokenattempter   r   r   get_access_token,   sH   

zAuthenticator.get_access_tokenc              
   C   s  z:t | jd*}t|}|ddt  kr&|dW  d   W S t	d t
ddd	1 s4w   Y  W n4 tyH   t	d
 Y n' tjtfyf } zt	dt|  W Y d}~nd}~w t
yn   Y nw z0|  }t | jd}t|| W d   n1 sw   Y  |d}|r|W S tddd	 ty } ztdt|  tdt| dd	d}~w ty } ztdt| dd	d}~ww )z
        Get the API key, refreshing if necessary.

        Returns:
            str: The GitHub Copilot API key.

        Raises:
            GetAPIKeyError: If unable to obtain an API key.
        r   Z
expires_atr   tokenNzAPI key expired, refreshingzAPI key expiredr!   r"   z+No API key file found or error opening filez!Error reading API key from file: r    zAPI key response missing tokenzError saving API key to file: zFailed to save API key: i  zFailed to refresh API key: )r%   r   jsonloadgetr   now	timestampr   r)   r	   r(   JSONDecodeErrorKeyErrorr/   _refresh_api_keydumpr   r.   r   )r   r0   api_key_infor3   r5   r   r   r   get_api_keyS   s\   



 
zAuthenticator.get_api_keyc              
   C   s   z,t | jd}t|}|di }|d}|W  d   W S 1 s%w   Y  W dS  ttjtfyM } zt	dt
|  W Y d}~dS d}~ww )z
        Get the API endpoint from the api-key.json file.

        Returns:
            Optional[str]: The GitHub Copilot API endpoint, or None if not found.
        r   	endpointsapiNz&Error reading API endpoint from file: )r%   r   r6   r7   r8   r(   r;   r<   r   r)   r/   )r   r0   r?   rA   Zapi_endpointr3   r   r   r   get_api_base   s   

(zAuthenticator.get_api_basec           	      C   s   |   }| |}d}t|D ]g}z%t }|jt|d}|  | }d|v r-|W   S t	d|  W q t
jy[ } ztd|d  d| dt|  W Y d	}~qd	}~w tyv } ztd
t|  W Y d	}~qd	}~ww tddd)z
        Refresh the API key using the access token.

        Returns:
            Dict[str, Any]: The API key information including token and expiration.

        Raises:
            RefreshAPIKeyError: If unable to refresh the API key.
        r   )headersr5   z API key response missing token: z'HTTP error refreshing API key (attempt r   /z): Nz%Unexpected error refreshing API key: z/Failed to refresh API key after maximum retriesr!   r"   )r4   _get_github_headersr*   r   r8   GITHUB_API_KEY_URLraise_for_statusr6   r   r)   httpxHTTPStatusErrorr.   r/   	Exceptionr   )	r   r1   rD   max_retriesr2   sync_clientresponseZresponse_jsonr3   r   r   r   r=      s6   


 zAuthenticator._refresh_api_keyc                 C   s&   t j| jst j| jdd dS dS )z"Ensure the token directory exists.T)exist_okN)r   r   existsr   makedirsr   r   r   r   r      s   zAuthenticator._ensure_token_dirr1   c                 C   s6   dddddd}|rd| |d< d	|vrd|d	< |S )
z
        Generate standard GitHub headers for API requests.

        Args:
            access_token: Optional access token to include in the headers.

        Returns:
            Dict[str, str]: Headers for GitHub API requests.
        zapplication/jsonzvscode/1.85.1zcopilot/1.155.0zGithubCopilot/1.155.0zgzip,deflate,br)acceptzeditor-versionzeditor-plugin-versionz
user-agentzaccept-encodingztoken authorizationzcontent-typer   )r   r1   rD   r   r   r   rF      s   z!Authenticator._get_github_headersc              
      s8  z8t  }|jt|  tddd}|  |  g d}t fdd|D s6t	d   t
dd	d
 W S  tjyZ } zt	dt|  t
dt| d	d
d}~w tjy{ } zt	dt|  t
dt| d	d
d}~w ty } zt	dt|  t
dt| d	d
d}~ww )z
        Get a device code for GitHub authentication.

        Returns:
            Dict[str, str]: Device code information.

        Raises:
            GetDeviceCodeError: If unable to get a device code.
        z	read:user)	client_idscoperD   r6   )device_code	user_codeverification_uric                 3   s    | ]}| v V  qd S Nr   ).0field	resp_jsonr   r   	<genexpr>   s    z1Authenticator._get_device_code.<locals>.<genexpr>z"Response missing required fields: z Response missing required fields  r"   z HTTP error getting device code: zFailed to get device code: NError decoding JSON response: z'Failed to decode device code response: z&Unexpected error getting device code: )r   postGITHUB_DEVICE_CODE_URLrF   GITHUB_CLIENT_IDrH   r6   allr   r.   r   rI   rJ   r/   r;   rK   )r   rM   respZrequired_fieldsr3   r   r]   r   _get_device_code   sN   
zAuthenticator._get_device_coderW   c                 C   s  t  }d}t|D ]}zJ|jt|  t|ddd}|  | }d|v r2t	d |d W   S d|v rL|
ddkrLtd	|d
  d| d ntd|  W nc tjyw } ztdt|  tdt| ddd}~w tjy } ztdt|  tdt| ddd}~w ty } ztdt|  tdt| ddd}~ww td q	tddd)a  
        Poll for an access token after user authentication.

        Args:
            device_code: The device code to use for polling.

        Returns:
            str: The access token.

        Raises:
            GetAccessTokenError: If unable to get an access token.
           z,urn:ietf:params:oauth:grant-type:device_code)rT   rW   Z
grant_typerV   r1   zAuthentication successful!r.   Zauthorization_pendingzAuthorization pending (attempt r   rE   )zUnexpected response: z%HTTP error polling for access token: zFailed to get access token: r`   r"   Nra   z(Failed to decode access token response: z+Unexpected error polling for access token:    z2Timed out waiting for user to authorize the device)r   r*   rb   GITHUB_ACCESS_TOKEN_URLrF   rd   rH   r6   r   infor8   r+   r)   rI   rJ   r.   r/   r
   r;   rK   timesleep)r   rW   rM   Zmax_attemptsr2   rf   r^   r3   r   r   r   _poll_for_access_token  sj   	
	z$Authenticator._poll_for_access_tokenc                 C   sD   |   }|d }|d }|d }td| d| ddd | |S )	a  
        Login to GitHub Copilot using device code flow.

        Returns:
            str: The GitHub access token.

        Raises:
            GetDeviceCodeError: If unable to get a device code.
            GetAccessTokenError: If unable to get an access token.
        rW   rX   rY   zPlease visit z and enter code z to authenticate.T)flush)rg   printro   )r   Zdevice_code_inforW   rX   rY   r   r   r   r,   U  s   
zAuthenticator._login)r   NrZ   )__name__
__module____qualname__r   r/   r4   r@   r   rC   r   r   r=   r   rF   rg   ro   r,   r   r   r   r   r      s    
'4
( 0Fr   )r6   r   rm   r   typingr   r   r   rI   Zlitellm._loggingr   Z&litellm.llms.custom_httpx.http_handlerr   Zcommon_utilsr	   r
   r   r   r   rd   rc   rk   rG   r   r   r   r   r   <module>   s    	