o
    ȳg                     @  s   d Z ddlmZ ddlZddlmZmZmZmZm	Z	 ddl
Z
ddlmZ ddlmZmZmZ er<ddlmZ ddlmZ dddZG dd deZdS )zUtil that calls GitHub.    )annotationsN)TYPE_CHECKINGAnyDictListOptional)get_from_dict_or_env)	BaseModel
ConfigDictmodel_validator)Issue)PullRequestreturnr   c                  C  s&   zddl } W | S  ty   tdw )zImport tiktoken.r   NzHtiktoken is not installed. Please install it with `pip install tiktoken`)tiktokenImportError)r    r   `/var/www/html/chatdoc2/venv/lib/python3.10/site-packages/langchain_community/utilities/github.py_import_tiktoken   s   
r   c                   @  s  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Z	ded	< dZ
ded
< dZded< eddZeddedbddZdcddZddddZdedd Zded!d"Zded#d$Zdfd&d'Zded(d)Zdgd+d,Zded-d.Zdhd0d1Zdid3d4Zdjd8d9Zdkd<d=Zdld>d?ZdmdAdBZdndDdEZ dodGdHZ!dpdJdKZ"dodLdMZ#dpdNdOZ$dqdQdRZ%dqdSdTZ&drdVdWZ'dedXdYZ(dedZd[Z)dsd]d^Z*dtd`daZ+dS )uGitHubAPIWrapperzWrapper for GitHub API.Nr   githubgithub_repo_instancezOptional[str]github_repositorygithub_app_idgithub_app_private_keyactive_branchgithub_base_branchforbid)extrabefore)modevaluesr   r   c              
   C  s|  t |dd}t |dd}t |dd}z
ddlm}m} W n ty'   td	w zt|d
}| }W d   n1 s=w   Y  W n tyN   |}Y nw |||}	||	d}
|
	 }|skt
d| d| dz|d }W n t
y } zt
d| d}~ww | }||}t |dd|jd}t |dd|jd}||d< ||d< ||d< ||d< ||d< ||d< ||d< |S )z?Validate that api key and python package exists in environment.r   GITHUB_REPOSITORYr   GITHUB_APP_IDr   GITHUB_APP_PRIVATE_KEYr   )AuthGithubIntegrationzHPyGithub is not installed. Please install it with `pip install PyGithub`rN)authz;Please make sure to install the created github app with id z on the repo: zrMore instructions can be found at https://docs.github.com/en/apps/using-github-apps/installing-your-own-github-appzBPlease make sure to give correct github parameters Error message: r   GITHUB_BASE_BRANCH)defaultr   ACTIVE_BRANCHr   r   )r   r   r$   r%   r   openread	ExceptionAppAuthget_installations
ValueErrorget_github_for_installationget_repodefault_branch)clsr    r   r   r   r$   r%   fprivate_keyr'   giinstallationegrepor   r   r   r   r   validate_environment,   s   


z%GitHubAPIWrapper.validate_environmentissuesList[Issue]
List[dict]c                 C  sT   g }|D ]#}|j }|j}|jr|jjnd}||d}|dur"||d< || q|S )
        Extracts title and number from each Issue and puts them in a dictionary
        Parameters:
            issues(List[Issue]): A list of Github Issue objects
        Returns:
            List[dict]: A dictionary of issue titles and numbers
        N)titlenumber	opened_by)rA   rB   userloginappend)selfr=   parsedissuerA   rB   rC   
issue_dictr   r   r   parse_issues|   s   
zGitHubAPIWrapper.parse_issuespull_requestsList[PullRequest]c                 C  s6   g }|D ]}| |j|jt|jt|jd q|S )r@   )rA   rB   commitscomments)rF   rA   rB   strrN   rO   )rG   rL   rH   prr   r   r   parse_pull_requests   s   z$GitHubAPIWrapper.parse_pull_requestsrP   c                 C  sN   | j jdd}dd |D }|r%| |}dtt| d t| }|S dS )z
        Fetches all open issues from the repo excluding pull requests

        Returns:
            str: A plaintext report containing the number of issues
            and each issue's title and number.
        r+   statec                 S  s   g | ]}|j s|qS r   )pull_request).0rI   r   r   r   
<listcomp>   s    z/GitHubAPIWrapper.get_issues.<locals>.<listcomp>Found z	 issues:
zNo open issues available)r   
get_issuesrK   rP   len)rG   r=   parsed_issuesparsed_issues_strr   r   r   rY      s   
zGitHubAPIWrapper.get_issuesc                 C  sF   | j jdd}|jdkr!| |}dtt| d t| }|S dS )z
        Fetches all open PRs from the repo

        Returns:
            str: A plaintext report containing the number of PRs
            and each PR's title and number.
        r+   rS   r   rX   z pull requests:
zNo open pull requests available)r   	get_pulls
totalCountrR   rP   rZ   )rG   rL   
parsed_prsparsed_prs_strr   r   r   list_open_pull_requests   s   	

z(GitHubAPIWrapper.list_open_pull_requestsc              
   C  s   g }z8| j jd| jd}|D ]}|jdkr|| |j q||j q|r8d|}dt	| d| W S W dS  t
yP } z
t|W  Y d}~S d}~ww )	z
        Fetches all files in the main branch of the repo.

        Returns:
            str: A plaintext report containing the paths and names of the files.
         refdir
rX   z files in the main branch:
z!No files found in the main branchN)r   get_contentsr   typeextend_list_filespathrF   joinrZ   r-   rP   rG   filescontentscontent	files_strr9   r   r   r   list_files_in_main_branch   s"   

z*GitHubAPIWrapper.list_files_in_main_branchbranch_namec                 C  sB   dd | j  D }||v r|| _d| dS d| dt| S )zEquivalent to `git checkout branch_name` for this Agent.
        Clones formatting from Github.

        Returns an Error (as a string) if branch doesn't exist.
        c                 S     g | ]}|j qS r   namerV   branchr   r   r   rW          z6GitHubAPIWrapper.set_active_branch.<locals>.<listcomp>zSwitched to branch ``zError z/ does not exist,in repo with current branches: )r   get_branchesr   rP   )rG   rs   curr_branchesr   r   r   set_active_branch   s   z"GitHubAPIWrapper.set_active_branchc              
   C  sl   zdd | j  D }|rd|}dt| d| W S W dS  ty5 } z
t|W  Y d}~S d}~ww )z
        Fetches a list of all branches in the repository.

        Returns:
            str: A plaintext report containing the names of the branches.
        c                 S  rt   r   ru   rw   r   r   r   rW      ry   z:GitHubAPIWrapper.list_branches_in_repo.<locals>.<listcomp>rf   rX   z branches in the repository:
z#No branches found in the repositoryN)r   r{   rl   rZ   r-   rP   )rG   branchesbranches_strr9   r   r   r   list_branches_in_repo   s   
z&GitHubAPIWrapper.list_branches_in_repoproposed_branch_namec                 C  s   ddl m} d}|}| j| jj}tdD ]S}z| jjd| |jjd || _	d| dW   S  |yi } z,|j
dkrQd	|jd
 v rQ|d7 }| d| }ntd|  td| W Y d}~qd}~ww d| dS )a5  
        Create a new branch, and set it as the active bot branch.
        Equivalent to `git switch -c proposed_branch_name`
        If the proposed branch already exists, we append _v1 then _v2...
        until a unique name is found.

        Returns:
            str: A plaintext success message.
        r   GithubExceptioni  zrefs/heads/)rd   shazBranch 'z9' created successfully, and set as current active branch.i  zReference already existsmessage   _vz Failed to create branch. Error: z8Unable to create branch name from proposed_branch_name: NzeUnable to create branch. At least 1000 branches exist with named derived from proposed_branch_name: `rz   )r   r   r   
get_branchr3   rangecreate_git_refcommitr   r   statusdataprintr-   )rG   r   r   inew_branch_namebase_branchr9   r   r   r   create_branch  s>   

zGitHubAPIWrapper.create_branchc              
   C  s   g }zA| j jd| jd}|D ]}|jdkr|| |j q||j q|r<d|}dt	| d| j d| W S d| j d	W S  t
yZ } zd
| W  Y d}~S d}~ww )z
        Fetches all files in the active branch of the repo,
        the branch the bot uses to make changes.

        Returns:
            str: A plaintext list containing the filepaths in the branch.
        rb   rc   re   rf   rX   z files in branch `z`:
zNo files found in branch: `rz   zError: N)r   rg   r   rh   ri   rj   rk   rF   rl   rZ   r-   rm   r   r   r   list_files_in_bot_branch8  s(   

z)GitHubAPIWrapper.list_files_in_bot_branchdirectory_pathc              
   C  sV   ddl m} zt| |W S  |y* } zd|j d|j W  Y d}~S d}~ww )z
        Recursively fetches files from a directory in the repo.

        Parameters:
            directory_path (str): Path to the directory

        Returns:
            str: List of file paths, or an error message.
        r   r   zError: status code z, N)r   r   rP   rj   r   r   )rG   r   r   r9   r   r   r   get_files_from_directoryV  s   
 z)GitHubAPIWrapper.get_files_from_directory	List[str]c                 C  sN   g }| j j|| jd}|D ]}|jdkr|| |j q||j q|S )Nrc   re   )r   rg   r   rh   ri   rj   rk   rF   )rG   r   rn   ro   rp   r   r   r   rj   g  s   
zGitHubAPIWrapper._list_filesissue_numberintDict[str, Any]c                 C  s   | j j|d}d}g }t|dkr9| |}t|dkrn|D ]}||j|jjd q!|d7 }t|dksd}|jrF|jjrF|jj}||j	|jt
|t
|dS )aK  
        Fetches a specific issue and its first 10 comments
        Parameters:
            issue_number(int): The number for the github issue
        Returns:
            dict: A dictionary containing the issue's title,
            body, comments as a string, and the username of the user
            who opened the issue
        rB   r   
   bodyrD   r   N)rB   rA   r   rO   rC   )r   	get_issuerZ   get_commentsget_pagerF   r   rD   rE   rA   rP   )rG   r   rI   pagerO   comments_pagecommentrC   r   r   r   r   u  s(   
zGitHubAPIWrapper.get_issue	pr_numberList[Dict[str, Any]]c                 C  sP  t  }d}g }| jjt|d}d}d}	 | |}t|dkr%	 |S |D ]{}	z`t|	j	}
|
j
dkr>t|
jd }ntd|	j	 d W q't|}|j
dkrW|j}ntd	|j
 d
 W q't|d||	j d }|| |k r||	j||	j|	jd ||7 }W q' ty } ztd|  W Y d}~q'd}~ww |d7 }q)av  Fetches the full text of all files in a PR. Truncates after first 3k tokens.
        # TODO: Enhancement to summarize files with ctags if they're getting long.

        Args:
            pr_number(int): The number of the pull request on Github

        Returns:
            dict: A dictionary containing the issue's title,
            body, and comments as a string
        i  r   r   T   download_urlzFailed to download file: z
, skippingz'Failed downloading file content (Error z). Skippingcl100k_basezfile_name file_contents)filenamero   	additions	deletionsz.Error when reading files from a PR on github. Nr   )r   r   get_pullr   	get_filesr   rZ   requestsgetcontents_urlstatus_codejsonloadstextr   get_encodingencoder   rF   r   r   r-   )rG   r   r   MAX_TOKENS_FOR_FILESpr_filesrQ   total_tokensr   
files_pagefilefile_metadata_responser   file_content_responsefile_contentfile_tokensr9   r   r   r   list_pull_request_files  sd   )




z(GitHubAPIWrapper.list_pull_request_filesc                   s  d| j j|d}dddd	 d fdd}i }||d|j ||dt| ||d|jr3|jnd g }d}t|dkr|| |}t|dkrNn.|D ]!}t|j|jj	d}	 |	 krf n|
|	  |	7 qP|d7 }t|dks@||dt| g }
d}t|
dkr| |}t|dkrn,|D ]}td|jji} | kr n|

|  |7 q|d7 }t|
dks||dt|
 |S )a}  
        Fetches a specific pull request and its first 10 comments,
        limited by max_tokens.

        Parameters:
            pr_number(int): The number for the Github pull
            max_tokens(int): The maximum number of tokens in the response
        Returns:
            dict: A dictionary containing the pull's title, body,
            and comments as a string
        i  r   r   r   rP   r   r   c                 S  s   t  }t|d| S )Nr   )r   rZ   r   r   )r   r   r   r   r   
get_tokens  s   z5GitHubAPIWrapper.get_pull_request.<locals>.get_tokens	data_dictr   keyvalueNonec                   s,    |}| kr|| |< |7 d S d S Nr   )r   r   r   tokensr   
max_tokensr   r   r   add_to_dict  s
   z6GitHubAPIWrapper.get_pull_request.<locals>.add_to_dictrA   rB   r   rb   r   r   r   rO   r   rN   N)r   rP   r   r   )r   r   r   rP   r   rP   r   r   )r   r   rA   rP   r   rZ   get_issue_commentsr   rD   rE   rF   get_commitsr   r   )rG   r   pullr   response_dictrO   r   r   r   comment_strrN   commits_pager   
commit_strr   r   r   get_pull_request  sP   


z!GitHubAPIWrapper.get_pull_requestpr_queryc              
   C  s   | j | jkrdS z$|dd }|t|d d }| jj||| j| j d}d|j W S  tyD } zdt| W  Y d}~S d}~ww )	a  
        Makes a pull request from the bot's branch to the base branch
        Parameters:
            pr_query(str): a string which contains the PR title
            and the PR body. The title is the first line
            in the string, and the body are the rest of the string.
            For example, "Updated README
made changes to add info"
        Returns:
            str: A success or failure message
        zaCannot make a pull request because 
            commits are already in the main or master branch.rf   r      N)rA   r   headbasezSuccessfully created PR number z*Unable to make pull request due to error:
)	r   r   splitrZ   r   create_pullrB   r-   rP   )rG   r   rA   r   rQ   r9   r   r   r   create_pull_request  s    z$GitHubAPIWrapper.create_pull_requestcomment_queryc              
   C  s   t |dd }|tt|d d }z| jj|d}|| dt| W S  ty@ } zdt| W  Y d}~S d}~ww )af  
        Adds a comment to a github issue
        Parameters:
            comment_query(str): a string which contains the issue number,
            two newlines, and the comment.
            for example: "1

Working on it now"
            adds the comment "working on it now" to issue 1
        Returns:
            str: A success or failure message
        z

r   r   Nr   zCommented on issue z%Unable to make comment due to error:
)r   r   rZ   rP   r   r   create_commentr-   )rG   r   r   r   rI   r9   r   r   r   comment_on_issue0  s   
z!GitHubAPIWrapper.comment_on_issue
file_queryc              
   C  s   | j | jkrd| j dS |dd }|t|d d }z5z| jj|| j d}|r7d| d	| j  d
W W S W n	 tyA   Y nw | jj|d| || j d d| W S  tyk } zdt| W  Y d}~S d}~ww )a  
        Creates a new file on the Github repo
        Parameters:
            file_query(str): a string which contains the file path
            and the file contents. The file path is the first line
            in the string, and the contents are the rest of the string.
            For example, "hello_world.md
# Hello World!"
        Returns:
            str: A success or failure message
        z2You're attempting to commit to the directly to theF branch, which is protected. Please create a new branch and try again.rf   r   r   Nrc   zFile already exists at `z` on branch `z+`. You must use `update_file` to modify it.zCreate )rk   r   rp   rx   zCreated file z"Unable to make file due to error:
)	r   r   r   rZ   r   rg   r-   create_filerP   )rG   r   	file_pathfile_contentsr   r9   r   r   r   r   D  s@   
zGitHubAPIWrapper.create_filer   c              
   C  sb   z| j j|| jd}|jdW S  ty0 } zd| d| j dt| W  Y d}~S d}~ww )a  
        Read a file from this agent's branch, defined by self.active_branch,
        which supports PR branches.
        Parameters:
            file_path(str): the file path
        Returns:
            str: The file decoded as a string, or an error message if not found
        rc   zutf-8zFile not found `z` on branch`z
`. Error: N)r   rg   r   decoded_contentdecoder-   rP   )rG   r   r   r9   r   r   r   	read_filer  s   	zGitHubAPIWrapper.read_filec              
   C  s   | j | jkrd| j dS zU|dd }|dd dd  }|dd d	d  }| |}|||}||krD	 W d
S | jj|dt| || j | jj	|| j dj
d dt| W S  tyz } zdt| W  Y d}~S d}~ww )a9  
        Updates a file with new content.
        Parameters:
            file_query(str): Contains the file path and the file contents.
                The old file contents is wrapped in OLD <<<< and >>>> OLD
                The new file contents is wrapped in NEW <<<< and >>>> NEW
                For example:
                /test/hello.txt
                OLD <<<<
                Hello Earth!
                >>>> OLD
                NEW <<<<
                Hello Mars!
                >>>> NEW
        Returns:
            A success or failure message
        2You're attempting to commit to the directlyto the r   rf   r   zOLD <<<<r   z>>>> OLDzNEW <<<<z>>>> NEWzFile content was not updated because old content was not found.It may be helpful to use the read_file action to get the current file contents.zUpdate rc   )rk   r   rp   rx   r   zUpdated file z$Unable to update file due to error:
N)r   r   r   stripr   replacer   update_filerP   rg   r   r-   )rG   r   r   old_file_contentsnew_file_contentsr   updated_file_contentr9   r   r   r   r     sB   

	zGitHubAPIWrapper.update_filec              
   C  s   | j | jkrd| j dS z| jj|d| | j | jj|| j djd d| W S  ty? } zdt| W  Y d}~S d}~ww )	z
        Deletes a file from the repo
        Parameters:
            file_path(str): Where the file is
        Returns:
            str: Success or failure message
        r   r   zDelete rc   )rk   r   rx   r   zDeleted file z$Unable to delete file due to error:
N)r   r   r   delete_filerg   r   r-   rP   )rG   r   r9   r   r   r   r     s(   
zGitHubAPIWrapper.delete_filequeryc              	   C  sj   | j j|| jd}td|j}d| dg}|d| D ]}|d|j d|j d|j  qd		|S )
z
        Searches issues and pull requests in the repository.

        Parameters:
            query(str): The search query

        Returns:
            str: A string containing the first 5 issues and pull requests
        )r;      Top 	 results:NTitle: z
, Number: z	, State: rf   )
r   search_issuesr   minr^   rF   rA   rB   rT   rl   )rG   r   search_result	max_itemsresultsrI   r   r   r   search_issues_and_prs  s   

z&GitHubAPIWrapper.search_issues_and_prsc                 C  s   | j j|| jd}|jdkrdS td|j}d| d|j dg}d}|D ]&}||kr- n| jj|j| jdj	
 }|d	|j d
| d |d7 }q%d|S )z
        Searches code in the repository.
        # Todo: limit total tokens returned...

        Parameters:
            query(str): The search query

        Returns:
            str: A string containing, at most, the top 5 search results
        )r   r;   r   z0 results found.r   zShowing top z of r   rc   zFilepath: `z`
File contents: z
<END OF FILE>r   rf   )r   search_coder   r^   r   r   rg   rk   r   r   r   rF   rl   )rG   r   r   max_resultsr   countcoder   r   r   r   r     s.   



zGitHubAPIWrapper.search_codereviewer_usernamec              
     s    j jddd}t fdd|D d}|du rd j dS z|j|gd	 d
| d|j W S  tyG } zd| W  Y d}~S d}~ww )a;  
        Creates a review request on *THE* open pull request
        that matches the current active_branch.

        Parameters:
            reviewer_username(str): The username of the person who is being requested

        Returns:
            str: A message confirming the creation of the review request
        r+   created)rT   sortc                 3  s"    | ]}|j j jkr|V  qd S r   )r   rd   r   )rV   rQ   rG   r   r   	<genexpr>  s     z9GitHubAPIWrapper.create_review_request.<locals>.<genexpr>Nz3No open pull request found for the current branch `rz   )	reviewersz Review request created for user z on PR #z-Failed to create a review request with error )r   r]   nextr   create_review_requestrB   r-   )rG   r   rL   rQ   r9   r   r   r   r    s*   z&GitHubAPIWrapper.create_review_requestc                 C  s&   | j  }d|j d|j d|j S )zu
        Fetches the latest release of the repository.

        Returns:
            str: The latest release
        zLatest title:  tag:  body: )r   get_latest_releaserA   tag_namer   )rG   releaser   r   r   r  0  s   

z#GitHubAPIWrapper.get_latest_releasec              	   C  sb   | j  }td|j}d| dg}|d| D ]}|d|j d|j d|j  qd|S )	zi
        Fetches all releases of the repository.

        Returns:
            str: The releases
        r   r   r   Nr   z, Tag: z, Body: rf   )	r   get_releasesr   r^   rF   rA   r  r   rl   )rG   releasesr   r   r  r   r   r   r	  >  s   


zGitHubAPIWrapper.get_releasesr  c                 C  s(   | j |}d|j d|j d|j S )z
        Fetches a specific release of the repository.

        Parameters:
            tag_name(str): The tag name of the release

        Returns:
            str: The release
        z	Release: r  r  )r   get_releaserA   r  r   )rG   r  r  r   r   r   r  Q  s   

zGitHubAPIWrapper.get_releaser   c                 C  s  |dkrt | t|S |dkrt | t|S |dkr*t | t|S |dkr2|  S |dkr;| |S |dkrD| |S |dkrM| 	|S |dkrV| 
|S |d	kr_| |S |d
krh| |S |dkrp|  S |dkrx|  S |dkr|  S |dkr|  S |dkr| |S |dkr| |S |dkr| |S |dkr| |S |dkr| |S |dkr| |S |dkr|  S |dkr|  S |dkr| |S td| )Nr   r   r   rY   r   r   r   r   r   r   ra   rr   r   r   r}   r   r   r   r   r  r  r	  r  zInvalid mode)r   dumpsr   r   r   r   rY   r   r   r   r   r   r   ra   rr   r   r   r}   r   r   r   r   r  r  r	  r  r0   )rG   r   r   r   r   r   runb  s^   












zGitHubAPIWrapper.run)r    r   r   r   )r=   r>   r   r?   )rL   rM   r   r?   )r   rP   )rs   rP   r   rP   )r   rP   r   rP   )r   rP   r   rP   )r   rP   r   r   )r   r   r   r   )r   r   r   r   )r   r   r   r   )r   rP   r   rP   )r   rP   r   rP   )r   rP   r   rP   )r   rP   r   rP   )r   rP   r   rP   )r   rP   r   rP   )r  rP   r   rP   )r   rP   r   rP   r   rP   ),__name__
__module____qualname____doc__r   __annotations__r   r   r   r   r   r   r
   model_configr   classmethodr<   rK   rR   rY   ra   rr   r}   r   r   r   r   rj   r   r   r   r   r   r   r   r   r   r   r   r  r  r	  r  r  r   r   r   r   r      sT   
 
N







,



!
?
?


.

:


!
!

r   )r   r   )r  
__future__r   r   typingr   r   r   r   r   r   langchain_core.utilsr   pydanticr	   r
   r   github.Issuer   github.PullRequestr   r   r   r   r   r   r   <module>   s    
