codespector 0.1.1__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codespector/base.py +7 -0
- codespector/codespector.py +84 -0
- codespector/errors.py +14 -0
- codespector/{local/prepare.py → local.py} +10 -11
- codespector/main.py +19 -24
- codespector/reviewer.py +84 -0
- codespector/types.py +50 -0
- {codespector-0.1.1.dist-info → codespector-0.2.0.dist-info}/METADATA +1 -2
- codespector-0.2.0.dist-info/RECORD +13 -0
- codespector/clients/__init__.py +0 -0
- codespector/clients/codestral.py +0 -5
- codespector/controller.py +0 -44
- codespector/local/__init__.py +0 -1
- codespector/local/main.py +0 -35
- codespector/local/reviewer.py +0 -108
- codespector-0.1.1.dist-info/RECORD +0 -14
- {codespector-0.1.1.dist-info → codespector-0.2.0.dist-info}/WHEEL +0 -0
- {codespector-0.1.1.dist-info → codespector-0.2.0.dist-info}/entry_points.txt +0 -0
- {codespector-0.1.1.dist-info → codespector-0.2.0.dist-info}/licenses/LICENSE +0 -0
    
        codespector/base.py
    ADDED
    
    
| @@ -0,0 +1,84 @@ | |
| 1 | 
            +
            from codespector.base import BasePipe
         | 
| 2 | 
            +
            from codespector.local import CodeSpectorDataPreparer
         | 
| 3 | 
            +
            from codespector.reviewer import CodeSpectorReviewer
         | 
| 4 | 
            +
            from codespector.types import AgentInfo
         | 
| 5 | 
            +
             | 
| 6 | 
            +
             | 
| 7 | 
            +
            class CodeSpector:
         | 
| 8 | 
            +
                def __init__(
         | 
| 9 | 
            +
                    self,
         | 
| 10 | 
            +
                    chat_token: str,
         | 
| 11 | 
            +
                    chat_agent: str,
         | 
| 12 | 
            +
                    compare_branch: str,
         | 
| 13 | 
            +
                    system_content: str,
         | 
| 14 | 
            +
                    prompt_content: str,
         | 
| 15 | 
            +
                    data_preparer: CodeSpectorDataPreparer,
         | 
| 16 | 
            +
                    reviewer: CodeSpectorReviewer,
         | 
| 17 | 
            +
                    pipeline: list[BasePipe],
         | 
| 18 | 
            +
                    result_file: str,
         | 
| 19 | 
            +
                    output_dir: str,
         | 
| 20 | 
            +
                    chat_model: str | None = None,
         | 
| 21 | 
            +
                ):
         | 
| 22 | 
            +
                    self.chat_token = chat_token
         | 
| 23 | 
            +
                    self.chat_agent = chat_agent
         | 
| 24 | 
            +
                    self.compare_branch = compare_branch
         | 
| 25 | 
            +
                    self.output_dir = output_dir
         | 
| 26 | 
            +
                    self.system_content = system_content
         | 
| 27 | 
            +
                    self.prompt_content = prompt_content
         | 
| 28 | 
            +
                    self.data_preparer = data_preparer
         | 
| 29 | 
            +
                    self.reviewer = reviewer
         | 
| 30 | 
            +
                    self.pipeline = pipeline
         | 
| 31 | 
            +
                    self.result_file = result_file
         | 
| 32 | 
            +
                    self.chat_model = chat_model
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                @classmethod
         | 
| 35 | 
            +
                def create(
         | 
| 36 | 
            +
                    cls,
         | 
| 37 | 
            +
                    chat_token: str,
         | 
| 38 | 
            +
                    chat_agent: str,
         | 
| 39 | 
            +
                    compare_branch: str,
         | 
| 40 | 
            +
                    system_content: str,
         | 
| 41 | 
            +
                    prompt_content: str,
         | 
| 42 | 
            +
                    result_file: str,
         | 
| 43 | 
            +
                    output_dir: str,
         | 
| 44 | 
            +
                    chat_model: str | None = None,
         | 
| 45 | 
            +
                ) -> 'CodeSpector':
         | 
| 46 | 
            +
                    agent_info = AgentInfo.create(
         | 
| 47 | 
            +
                        chat_agent=chat_agent,
         | 
| 48 | 
            +
                        chat_token=chat_token,
         | 
| 49 | 
            +
                        chat_model=chat_model,
         | 
| 50 | 
            +
                    )
         | 
| 51 | 
            +
                    data_preparer = CodeSpectorDataPreparer(
         | 
| 52 | 
            +
                        output_dir=output_dir,
         | 
| 53 | 
            +
                        compare_branch=compare_branch,
         | 
| 54 | 
            +
                    )
         | 
| 55 | 
            +
                    reviewer = CodeSpectorReviewer(
         | 
| 56 | 
            +
                        diff_file=data_preparer.combined_file,
         | 
| 57 | 
            +
                        chat_token=chat_token,
         | 
| 58 | 
            +
                        chat_agent=chat_agent,
         | 
| 59 | 
            +
                        system_content=system_content,
         | 
| 60 | 
            +
                        output_dir=output_dir,
         | 
| 61 | 
            +
                        chat_model=chat_model,
         | 
| 62 | 
            +
                        agent_info=agent_info,
         | 
| 63 | 
            +
                        prompt_content=prompt_content,
         | 
| 64 | 
            +
                        result_file=result_file,
         | 
| 65 | 
            +
                    )
         | 
| 66 | 
            +
                    pipeline = [data_preparer, reviewer]
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    return CodeSpector(
         | 
| 69 | 
            +
                        chat_token=chat_token,
         | 
| 70 | 
            +
                        chat_agent=chat_agent,
         | 
| 71 | 
            +
                        chat_model=chat_model,
         | 
| 72 | 
            +
                        compare_branch=compare_branch,
         | 
| 73 | 
            +
                        system_content=system_content,
         | 
| 74 | 
            +
                        prompt_content=prompt_content,
         | 
| 75 | 
            +
                        data_preparer=data_preparer,
         | 
| 76 | 
            +
                        reviewer=reviewer,
         | 
| 77 | 
            +
                        pipeline=pipeline,
         | 
| 78 | 
            +
                        output_dir=output_dir,
         | 
| 79 | 
            +
                        result_file=result_file,
         | 
| 80 | 
            +
                    )
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                def review(self):
         | 
| 83 | 
            +
                    for pipe in self.pipeline:
         | 
| 84 | 
            +
                        pipe.start()
         | 
    
        codespector/errors.py
    ADDED
    
    | @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            class NotValidCfgError(Exception):
         | 
| 2 | 
            +
                """Exception raised when the configuration is not valid."""
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                def __init__(self, message):
         | 
| 5 | 
            +
                    super().__init__(message)
         | 
| 6 | 
            +
                    self.message = message
         | 
| 7 | 
            +
             | 
| 8 | 
            +
             | 
| 9 | 
            +
            class AppError(Exception):
         | 
| 10 | 
            +
                """Exception raised for application errors."""
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def __init__(self, message):
         | 
| 13 | 
            +
                    super().__init__(message)
         | 
| 14 | 
            +
                    self.message = message
         | 
| @@ -1,9 +1,11 @@ | |
| 1 | 
            -
            import  | 
| 1 | 
            +
            import json
         | 
| 2 2 | 
             
            import subprocess
         | 
| 3 3 | 
             
            import os
         | 
| 4 4 |  | 
| 5 | 
            +
            from codespector.base import BasePipe
         | 
| 5 6 |  | 
| 6 | 
            -
             | 
| 7 | 
            +
             | 
| 8 | 
            +
            class CodeSpectorDataPreparer(BasePipe):
         | 
| 7 9 | 
             
                def __init__(
         | 
| 8 10 | 
             
                    self,
         | 
| 9 11 | 
             
                    output_dir: str,
         | 
| @@ -38,18 +40,18 @@ class CodeSpectorDataPreparer: | |
| 38 40 | 
             
                    diff_json = {'diff': '\n'.join(filtered_diff)}
         | 
| 39 41 | 
             
                    diff_filepath = os.path.join(self.output_dir, self.diff_file)
         | 
| 40 42 | 
             
                    with open(diff_filepath, 'w', encoding='utf-8') as f:
         | 
| 41 | 
            -
                         | 
| 43 | 
            +
                        json.dump(diff_json, f, indent=4, ensure_ascii=False)
         | 
| 42 44 |  | 
| 43 45 | 
             
                    with open(os.path.join(self.output_dir, self.original_files_tmp), 'r', encoding='utf-8') as f:
         | 
| 44 | 
            -
                        original_files_data =  | 
| 46 | 
            +
                        original_files_data = json.load(f)
         | 
| 45 47 |  | 
| 46 48 | 
             
                    with open(diff_filepath, 'r', encoding='utf-8') as f:
         | 
| 47 | 
            -
                        diff_data =  | 
| 49 | 
            +
                        diff_data = json.load(f)
         | 
| 48 50 |  | 
| 49 51 | 
             
                    combined_data = {**original_files_data, **diff_data}
         | 
| 50 52 |  | 
| 51 53 | 
             
                    with open(os.path.join(self.output_dir, self.combined_file), 'w', encoding='utf-8') as f:
         | 
| 52 | 
            -
                         | 
| 54 | 
            +
                        json.dump(combined_data, f, indent=4, ensure_ascii=False)
         | 
| 53 55 |  | 
| 54 56 | 
             
                def _prepare_name_only_file(self):
         | 
| 55 57 | 
             
                    changed_files = subprocess.run(
         | 
| @@ -70,12 +72,9 @@ class CodeSpectorDataPreparer: | |
| 70 72 | 
             
                    filepath = os.path.join(self.output_dir, self.original_files_tmp)
         | 
| 71 73 |  | 
| 72 74 | 
             
                    with open(filepath, 'w', encoding='utf-8') as f:
         | 
| 73 | 
            -
                         | 
| 75 | 
            +
                        json.dump(result, f, indent=4, ensure_ascii=False)
         | 
| 74 76 |  | 
| 75 | 
            -
                def  | 
| 77 | 
            +
                def start(self):
         | 
| 76 78 | 
             
                    self._prepare_dir()
         | 
| 77 79 | 
             
                    self._prepare_name_only_file()
         | 
| 78 80 | 
             
                    self._prepare_diff_file()
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                def start(self):
         | 
| 81 | 
            -
                    self.prepare_data()
         | 
    
        codespector/main.py
    CHANGED
    
    | @@ -3,8 +3,7 @@ from pathlib import Path | |
| 3 3 | 
             
            import click
         | 
| 4 4 | 
             
            from environs import Env
         | 
| 5 5 |  | 
| 6 | 
            -
            from . | 
| 7 | 
            -
            from loguru import logger
         | 
| 6 | 
            +
            from codespector.codespector import CodeSpector
         | 
| 8 7 |  | 
| 9 8 | 
             
            BASE_PATH = Path(__file__).parent.parent
         | 
| 10 9 |  | 
| @@ -12,10 +11,23 @@ env = Env() | |
| 12 11 | 
             
            env.read_env(path=str(BASE_PATH / '.env'))
         | 
| 13 12 |  | 
| 14 13 |  | 
| 14 | 
            +
            @click.option(
         | 
| 15 | 
            +
                '--result-file',
         | 
| 16 | 
            +
                type=str,
         | 
| 17 | 
            +
                help='Set file for saving the result',
         | 
| 18 | 
            +
                envvar='CODESPECTOR_RESULT_FILE',
         | 
| 19 | 
            +
                show_envvar=True,
         | 
| 20 | 
            +
            )
         | 
| 21 | 
            +
            @click.option(
         | 
| 22 | 
            +
                '--prompt-content',
         | 
| 23 | 
            +
                type=str,
         | 
| 24 | 
            +
                help='Prompt content which included to review prompt',
         | 
| 25 | 
            +
                envvar='CODESPECTOR_PROMPT_CONTENT',
         | 
| 26 | 
            +
                show_envvar=True,
         | 
| 27 | 
            +
            )
         | 
| 15 28 | 
             
            @click.option(
         | 
| 16 29 | 
             
                '--system-content',
         | 
| 17 30 | 
             
                type=str,
         | 
| 18 | 
            -
                default='Ты код ревьювер. Отвечай на русском языке.',
         | 
| 19 31 | 
             
                envvar='CODESPECTOR_SYSTEM_CONTENT',
         | 
| 20 32 | 
             
                show_envvar=True,
         | 
| 21 33 | 
             
                help='Content which used in system field for agent',
         | 
| @@ -23,8 +35,8 @@ env.read_env(path=str(BASE_PATH / '.env')) | |
| 23 35 | 
             
            @click.option(
         | 
| 24 36 | 
             
                '--output-dir',
         | 
| 25 37 | 
             
                type=str,
         | 
| 26 | 
            -
                default='codespector',
         | 
| 27 38 | 
             
                envvar='CODESPECTOR_OUTPUT_DIR',
         | 
| 39 | 
            +
                default='codespector',
         | 
| 28 40 | 
             
                show_envvar=True,
         | 
| 29 41 | 
             
                help='Select the output directory',
         | 
| 30 42 | 
             
            )
         | 
| @@ -32,16 +44,13 @@ env.read_env(path=str(BASE_PATH / '.env')) | |
| 32 44 | 
             
                '-b',
         | 
| 33 45 | 
             
                '--compare-branch',
         | 
| 34 46 | 
             
                type=str,
         | 
| 35 | 
            -
                default='develop',
         | 
| 36 47 | 
             
                help='Select the branch to compare the current one with',
         | 
| 37 48 | 
             
            )
         | 
| 38 49 | 
             
            @click.option(
         | 
| 39 50 | 
             
                '--chat-agent',
         | 
| 40 | 
            -
                type=click.Choice(['codestral', 'chatgpt'], case_sensitive=False),
         | 
| 41 51 | 
             
                envvar='CODESPECTOR_CHAT_AGENT',
         | 
| 42 52 | 
             
                show_envvar=True,
         | 
| 43 | 
            -
                 | 
| 44 | 
            -
                help='Choose the chat agent to use',
         | 
| 53 | 
            +
                help='Choose the chat agent to use you can use one from [codestral, chatgpr, deepseek]. Or set yours chat agent',
         | 
| 45 54 | 
             
            )
         | 
| 46 55 | 
             
            @click.option(
         | 
| 47 56 | 
             
                '--chat-model',
         | 
| @@ -56,25 +65,11 @@ env.read_env(path=str(BASE_PATH / '.env')) | |
| 56 65 | 
             
                envvar='CODESPECTOR_CHAT_TOKEN',
         | 
| 57 66 | 
             
                show_envvar=True,
         | 
| 58 67 | 
             
            )
         | 
| 59 | 
            -
            @click.option(
         | 
| 60 | 
            -
                '--mode',
         | 
| 61 | 
            -
                type=click.Choice(['local'], case_sensitive=False),
         | 
| 62 | 
            -
                default='local',
         | 
| 63 | 
            -
                help='Choose the mode of the application',
         | 
| 64 | 
            -
            )
         | 
| 65 68 | 
             
            @click.version_option(message='%(version)s')
         | 
| 66 69 | 
             
            @click.command()
         | 
| 67 70 | 
             
            def main(*args, **kwargs):
         | 
| 68 | 
            -
                 | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
            def start(*args, **kwargs):
         | 
| 72 | 
            -
                codespector = CodeSpectorController(*args, **kwargs)
         | 
| 73 | 
            -
                try:
         | 
| 74 | 
            -
                    codespector.start()
         | 
| 75 | 
            -
                    logger.info('Review completed successfully.See result.txt in {} directory', kwargs['output_dir'])
         | 
| 76 | 
            -
                except Exception as e:
         | 
| 77 | 
            -
                    logger.error('Error while review: {}', e)
         | 
| 71 | 
            +
                codespector = CodeSpector.create(*args, **kwargs)
         | 
| 72 | 
            +
                codespector.review()
         | 
| 78 73 |  | 
| 79 74 |  | 
| 80 75 | 
             
            if __name__ == '__main__':
         | 
    
        codespector/reviewer.py
    ADDED
    
    | @@ -0,0 +1,84 @@ | |
| 1 | 
            +
            import os.path
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import json
         | 
| 4 | 
            +
            from urllib.error import HTTPError
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            import requests
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            from loguru import logger
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            from codespector.errors import AppError
         | 
| 11 | 
            +
            from codespector.types import AgentInfo
         | 
| 12 | 
            +
             | 
| 13 | 
            +
             | 
| 14 | 
            +
            class CodeSpectorReviewer:
         | 
| 15 | 
            +
                def __init__(
         | 
| 16 | 
            +
                    self,
         | 
| 17 | 
            +
                    diff_file: str,
         | 
| 18 | 
            +
                    chat_token: str,
         | 
| 19 | 
            +
                    chat_agent: str,
         | 
| 20 | 
            +
                    chat_model: str,
         | 
| 21 | 
            +
                    system_content: str,
         | 
| 22 | 
            +
                    prompt_content: str,
         | 
| 23 | 
            +
                    output_dir: str,
         | 
| 24 | 
            +
                    result_file: str,
         | 
| 25 | 
            +
                    agent_info: AgentInfo,
         | 
| 26 | 
            +
                ):
         | 
| 27 | 
            +
                    self.diff_file = diff_file
         | 
| 28 | 
            +
                    self.chat_token = chat_token
         | 
| 29 | 
            +
                    self.chat_agent = chat_agent
         | 
| 30 | 
            +
                    self.chat_model = chat_model
         | 
| 31 | 
            +
                    self.system_content = system_content
         | 
| 32 | 
            +
                    self.prompt_content = prompt_content
         | 
| 33 | 
            +
                    self.output_dir = output_dir
         | 
| 34 | 
            +
                    self.result_file = result_file
         | 
| 35 | 
            +
                    self.agent_info = agent_info
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    self.request_file = 'request.json'
         | 
| 38 | 
            +
                    self.response_file = 'response.json'
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def _request_to_chat_agent(self, prompt: str):
         | 
| 41 | 
            +
                    request_data = {
         | 
| 42 | 
            +
                        'model': self.agent_info.model,
         | 
| 43 | 
            +
                        'messages': [{'role': 'system', 'content': self.system_content}, {'role': 'user', 'content': prompt}],
         | 
| 44 | 
            +
                    }
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    with open(os.path.join(self.output_dir, self.request_file), 'w', encoding='utf-8') as f:
         | 
| 47 | 
            +
                        json.dump(request_data, f, indent=4, ensure_ascii=False)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    response = requests.post(
         | 
| 50 | 
            +
                        self.agent_info.url,
         | 
| 51 | 
            +
                        json=request_data,
         | 
| 52 | 
            +
                        headers=self.agent_info.headers,
         | 
| 53 | 
            +
                        timeout=100,
         | 
| 54 | 
            +
                    )
         | 
| 55 | 
            +
                    response.raise_for_status()
         | 
| 56 | 
            +
                    return response
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def send_to_review(self):
         | 
| 59 | 
            +
                    with open(os.path.join(self.output_dir, self.diff_file), 'r', encoding='utf-8') as f:
         | 
| 60 | 
            +
                        diff_data = json.load(f)
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    diff_content = diff_data.get('diff', '')
         | 
| 63 | 
            +
                    original_files = diff_data.get('original files', [])
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    original_files_str = json.dumps(original_files, indent=4, ensure_ascii=False)
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    prompt = f'{self.prompt_content}\n\nDIFF:\n{diff_content}\n\nORIGINAL FILES:\n{original_files_str}'
         | 
| 68 | 
            +
                    try:
         | 
| 69 | 
            +
                        response = self._request_to_chat_agent(prompt=prompt)
         | 
| 70 | 
            +
                    except HTTPError as e:
         | 
| 71 | 
            +
                        logger.error('Error while send request: {}', e)
         | 
| 72 | 
            +
                        raise AppError('Error while send request') from e
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    with open(os.path.join(self.output_dir, self.response_file), 'w', encoding='utf-8') as f:
         | 
| 75 | 
            +
                        json.dump(response.json(), f, indent=4, ensure_ascii=False)
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    resp = response.json()
         | 
| 78 | 
            +
                    clear_response = resp['choices'][0]['message']['content']
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    with open(os.path.join(self.output_dir, self.result_file), 'w', encoding='utf-8') as f:
         | 
| 81 | 
            +
                        f.write(clear_response)
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                def start(self):
         | 
| 84 | 
            +
                    self.send_to_review()
         | 
    
        codespector/types.py
    ADDED
    
    | @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            from dataclasses import dataclass
         | 
| 2 | 
            +
            from loguru import logger
         | 
| 3 | 
            +
            from codespector.errors import NotValidCfgError
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            AGENT_URL_MAPPING = {
         | 
| 6 | 
            +
                'codestral': 'https://api.mistral.ai/v1/chat/completions',
         | 
| 7 | 
            +
                'chatgpt': 'https://api.openai.com/v1/chat/completions',
         | 
| 8 | 
            +
                'deepseek': 'https://api.deepseek.com/v1/chat/completions',
         | 
| 9 | 
            +
            }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            DEFAULT_AGENT_MODEL = {
         | 
| 12 | 
            +
                'codestral': 'codestral-latest',
         | 
| 13 | 
            +
                'chatgpt': 'gpt-4o',
         | 
| 14 | 
            +
                'deepseek': 'deepseek-chat',
         | 
| 15 | 
            +
            }
         | 
| 16 | 
            +
             | 
| 17 | 
            +
             | 
| 18 | 
            +
            @dataclass
         | 
| 19 | 
            +
            class AgentInfo:
         | 
| 20 | 
            +
                model: str
         | 
| 21 | 
            +
                url: str
         | 
| 22 | 
            +
                headers: dict
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                @classmethod
         | 
| 25 | 
            +
                def create(
         | 
| 26 | 
            +
                    cls,
         | 
| 27 | 
            +
                    chat_agent: str,
         | 
| 28 | 
            +
                    chat_token: str,
         | 
| 29 | 
            +
                    chat_model: str | None = None,
         | 
| 30 | 
            +
                    completion_url: str | None = None,
         | 
| 31 | 
            +
                ) -> 'AgentInfo':
         | 
| 32 | 
            +
                    if completion_url:
         | 
| 33 | 
            +
                        url = completion_url
         | 
| 34 | 
            +
                    else:
         | 
| 35 | 
            +
                        url = AGENT_URL_MAPPING.get(chat_agent)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    if chat_model:
         | 
| 38 | 
            +
                        model = chat_model
         | 
| 39 | 
            +
                    else:
         | 
| 40 | 
            +
                        model = DEFAULT_AGENT_MODEL.get(chat_agent)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    if model is None or url is None:
         | 
| 43 | 
            +
                        raise NotValidCfgError('Invalid chat model or completions url')
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    headers = {'Authorization': f'Bearer {chat_token}'}
         | 
| 46 | 
            +
                    return cls(
         | 
| 47 | 
            +
                        url=url,
         | 
| 48 | 
            +
                        model=model,
         | 
| 49 | 
            +
                        headers=headers,
         | 
| 50 | 
            +
                    )
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            Metadata-Version: 2.4
         | 
| 2 2 | 
             
            Name: codespector
         | 
| 3 | 
            -
            Version: 0. | 
| 3 | 
            +
            Version: 0.2.0
         | 
| 4 4 | 
             
            Summary: Assistant for reviewing your code
         | 
| 5 5 | 
             
            Project-URL: Repository, https://github.com/Vladimir-Titov/codespector
         | 
| 6 6 | 
             
            Project-URL: Issues, https://github.com/Vladimir-Titov/codespector/issues
         | 
| @@ -15,7 +15,6 @@ Requires-Dist: click>=8.1.8 | |
| 15 15 | 
             
            Requires-Dist: environs>=14.1.1
         | 
| 16 16 | 
             
            Requires-Dist: loguru>=0.7.3
         | 
| 17 17 | 
             
            Requires-Dist: requests>=2.32.3
         | 
| 18 | 
            -
            Requires-Dist: ujson>=5.10.0
         | 
| 19 18 | 
             
            Description-Content-Type: text/markdown
         | 
| 20 19 |  | 
| 21 20 | 
             
            # CodeSpector
         | 
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            codespector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 2 | 
            +
            codespector/base.py,sha256=uPoJ8h0Dlna-eUXyyD9s7uaQ4z0pS6qPdQMeu10muEk,159
         | 
| 3 | 
            +
            codespector/codespector.py,sha256=qW9G3arSq3VzWxdvPP4uCMNFfWFd7HBqs8M8dQsOAHU,2588
         | 
| 4 | 
            +
            codespector/errors.py,sha256=Z5W6i7iG_iBj_IfdSZ_f9jij0oPxTZ2Cxs87kzk3oyU,377
         | 
| 5 | 
            +
            codespector/local.py,sha256=aEq1YBm4fAFywpwmTaAdpM0_C2RHuIFDmAKikUZkmtQ,2800
         | 
| 6 | 
            +
            codespector/main.py,sha256=dfCds8mEIVb7mHVoczijnceIOd94I9yJc-8ZWaXcllo,1707
         | 
| 7 | 
            +
            codespector/reviewer.py,sha256=WoKRO-bFFLKNUiWB_TWoK0rRjenHkJ5fej_lhcj_e2Q,2740
         | 
| 8 | 
            +
            codespector/types.py,sha256=i5PFazyUc5rVq5Fhn4_FsG7R1l617vyjByvnixDUw1Y,1255
         | 
| 9 | 
            +
            codespector-0.2.0.dist-info/METADATA,sha256=YNuCT3bCxjyw46_gFGQdqwTwTOhswYWMqFRTF7eFXD0,3131
         | 
| 10 | 
            +
            codespector-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
         | 
| 11 | 
            +
            codespector-0.2.0.dist-info/entry_points.txt,sha256=QlHn96KY8vzY1sOweKIuZOAQrSVse6h3v57vkwmHmJg,54
         | 
| 12 | 
            +
            codespector-0.2.0.dist-info/licenses/LICENSE,sha256=Eta34ENUL_dZWy-prVNucMkoA38WIqiO9pKTUYiuT_A,1070
         | 
| 13 | 
            +
            codespector-0.2.0.dist-info/RECORD,,
         | 
    
        codespector/clients/__init__.py
    DELETED
    
    | 
            File without changes
         | 
    
        codespector/clients/codestral.py
    DELETED
    
    
    
        codespector/controller.py
    DELETED
    
    | @@ -1,44 +0,0 @@ | |
| 1 | 
            -
            from codespector import local
         | 
| 2 | 
            -
            from loguru import logger
         | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
            class CodeSpectorController:
         | 
| 6 | 
            -
                __slots__ = (
         | 
| 7 | 
            -
                    'mode',
         | 
| 8 | 
            -
                    'chat_token',
         | 
| 9 | 
            -
                    'chat_agent',
         | 
| 10 | 
            -
                    'compare_branch',
         | 
| 11 | 
            -
                    'output_dir',
         | 
| 12 | 
            -
                    'system_content',
         | 
| 13 | 
            -
                    'chat_model',
         | 
| 14 | 
            -
                )
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                def __init__(
         | 
| 17 | 
            -
                    self,
         | 
| 18 | 
            -
                    mode: str,
         | 
| 19 | 
            -
                    chat_token: str,
         | 
| 20 | 
            -
                    chat_agent: str,
         | 
| 21 | 
            -
                    compare_branch: str,
         | 
| 22 | 
            -
                    output_dir: str,
         | 
| 23 | 
            -
                    system_content: str,
         | 
| 24 | 
            -
                    chat_model: str,
         | 
| 25 | 
            -
                ):
         | 
| 26 | 
            -
                    self.mode = mode
         | 
| 27 | 
            -
                    self.chat_token = chat_token
         | 
| 28 | 
            -
                    self.chat_agent = chat_agent
         | 
| 29 | 
            -
                    self.compare_branch = compare_branch
         | 
| 30 | 
            -
                    self.output_dir = output_dir
         | 
| 31 | 
            -
                    self.system_content = system_content
         | 
| 32 | 
            -
                    self.chat_model = chat_model
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                def start(self):
         | 
| 35 | 
            -
                    codespector = local.LocalCodespector(
         | 
| 36 | 
            -
                        chat_token=self.chat_token,
         | 
| 37 | 
            -
                        chat_agent=self.chat_agent,
         | 
| 38 | 
            -
                        compare_branch=self.compare_branch,
         | 
| 39 | 
            -
                        output_dir=self.output_dir,
         | 
| 40 | 
            -
                        system_content=self.system_content,
         | 
| 41 | 
            -
                        chat_model=self.chat_model,
         | 
| 42 | 
            -
                    )
         | 
| 43 | 
            -
                    codespector.review()
         | 
| 44 | 
            -
                    logger.info('Review completed successfully.See result.txt in {} directory', self.output_dir)
         | 
    
        codespector/local/__init__.py
    DELETED
    
    | @@ -1 +0,0 @@ | |
| 1 | 
            -
            from .main import LocalCodespector
         | 
    
        codespector/local/main.py
    DELETED
    
    | @@ -1,35 +0,0 @@ | |
| 1 | 
            -
            from .prepare import CodeSpectorDataPreparer
         | 
| 2 | 
            -
            from .reviewer import CodeSpectorReviewer
         | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
            class LocalCodespector:
         | 
| 6 | 
            -
                def __init__(
         | 
| 7 | 
            -
                    self,
         | 
| 8 | 
            -
                    chat_token: str,
         | 
| 9 | 
            -
                    chat_agent: str,
         | 
| 10 | 
            -
                    compare_branch: str,
         | 
| 11 | 
            -
                    output_dir: str,
         | 
| 12 | 
            -
                    system_content: str,
         | 
| 13 | 
            -
                    chat_model: str,
         | 
| 14 | 
            -
                ):
         | 
| 15 | 
            -
                    self.chat_token = chat_token
         | 
| 16 | 
            -
                    self.chat_agent = chat_agent
         | 
| 17 | 
            -
                    self.compare_branch = compare_branch
         | 
| 18 | 
            -
                    self.output_dir = output_dir
         | 
| 19 | 
            -
                    self.system_content = system_content
         | 
| 20 | 
            -
                    self.chat_model = chat_model
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                    self.data_preparer = CodeSpectorDataPreparer(output_dir=self.output_dir, compare_branch=self.compare_branch)
         | 
| 23 | 
            -
                    self.reviewer = CodeSpectorReviewer(
         | 
| 24 | 
            -
                        diff_file=self.data_preparer.combined_file,
         | 
| 25 | 
            -
                        chat_token=self.chat_token,
         | 
| 26 | 
            -
                        chat_agent=self.chat_agent,
         | 
| 27 | 
            -
                        system_content=self.system_content,
         | 
| 28 | 
            -
                        output_dir=self.output_dir,
         | 
| 29 | 
            -
                        chat_model=self.chat_model,
         | 
| 30 | 
            -
                    )
         | 
| 31 | 
            -
                    self.processes = [self.data_preparer, self.reviewer]
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                def review(self):
         | 
| 34 | 
            -
                    for process in self.processes:
         | 
| 35 | 
            -
                        process.start()
         | 
    
        codespector/local/reviewer.py
    DELETED
    
    | @@ -1,108 +0,0 @@ | |
| 1 | 
            -
            import os.path
         | 
| 2 | 
            -
            from dataclasses import dataclass
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            import ujson
         | 
| 5 | 
            -
            import requests
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            from loguru import logger
         | 
| 8 | 
            -
             | 
| 9 | 
            -
            AGENT_URL_MAPPING = {
         | 
| 10 | 
            -
                'codestral': 'https://api.mistral.ai/v1/chat/completions',
         | 
| 11 | 
            -
                'chatgpt': 'https://api.openai.com/v1/chat/completions',
         | 
| 12 | 
            -
            }
         | 
| 13 | 
            -
             | 
| 14 | 
            -
            DEFAULT_AGENT_MODEL = {'codestral': 'codestral-latest', 'chatgpt': 'gpt-4o'}
         | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
            @dataclass
         | 
| 18 | 
            -
            class AgentInfo:
         | 
| 19 | 
            -
                model: str
         | 
| 20 | 
            -
                url: str
         | 
| 21 | 
            -
                headers: dict
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                @classmethod
         | 
| 24 | 
            -
                def create(cls, chat_agent: str, chat_token: str, chat_model: str | None = None) -> 'AgentInfo':
         | 
| 25 | 
            -
                    url = AGENT_URL_MAPPING[chat_agent]
         | 
| 26 | 
            -
                    model = chat_model if chat_model else DEFAULT_AGENT_MODEL[chat_agent]
         | 
| 27 | 
            -
                    headers = {'Authorization': f'Bearer {chat_token}'}
         | 
| 28 | 
            -
                    return cls(
         | 
| 29 | 
            -
                        url=url,
         | 
| 30 | 
            -
                        model=model,
         | 
| 31 | 
            -
                        headers=headers,
         | 
| 32 | 
            -
                    )
         | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
            class CodeSpectorReviewer:
         | 
| 36 | 
            -
                def __init__(
         | 
| 37 | 
            -
                    self,
         | 
| 38 | 
            -
                    diff_file: str,
         | 
| 39 | 
            -
                    chat_token: str,
         | 
| 40 | 
            -
                    chat_agent: str,
         | 
| 41 | 
            -
                    chat_model: str | None,
         | 
| 42 | 
            -
                    system_content: str,
         | 
| 43 | 
            -
                    output_dir: str,
         | 
| 44 | 
            -
                ):
         | 
| 45 | 
            -
                    self.diff_file = diff_file
         | 
| 46 | 
            -
                    self.chat_token = chat_token
         | 
| 47 | 
            -
                    self.chat_agent = chat_agent
         | 
| 48 | 
            -
                    self.chat_model = chat_model
         | 
| 49 | 
            -
                    self.system_content = system_content
         | 
| 50 | 
            -
                    self.output_dir = output_dir
         | 
| 51 | 
            -
             | 
| 52 | 
            -
                    self.request_file = 'request.json'
         | 
| 53 | 
            -
                    self.response_file = 'response.json'
         | 
| 54 | 
            -
                    self.result_file = 'result.txt'
         | 
| 55 | 
            -
             | 
| 56 | 
            -
                def _request_to_chat_agent(self, prompt: str):
         | 
| 57 | 
            -
                    agent_info = AgentInfo.create(self.chat_agent, self.chat_token, self.chat_model)
         | 
| 58 | 
            -
                    request_data = {
         | 
| 59 | 
            -
                        'model': agent_info.model,
         | 
| 60 | 
            -
                        'messages': [{'role': 'system', 'content': self.system_content}, {'role': 'user', 'content': prompt}],
         | 
| 61 | 
            -
                    }
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                    with open(os.path.join(self.output_dir, self.request_file), 'w', encoding='utf-8') as f:
         | 
| 64 | 
            -
                        ujson.dump(request_data, f, indent=4, ensure_ascii=False)
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                    response = requests.post(
         | 
| 67 | 
            -
                        agent_info.url,
         | 
| 68 | 
            -
                        json=request_data,
         | 
| 69 | 
            -
                        headers=agent_info.headers,
         | 
| 70 | 
            -
                        timeout=100,
         | 
| 71 | 
            -
                    )
         | 
| 72 | 
            -
                    response.raise_for_status()
         | 
| 73 | 
            -
                    return response
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                def send_to_review(self):
         | 
| 76 | 
            -
                    with open(os.path.join(self.output_dir, self.diff_file), 'r', encoding='utf-8') as f:
         | 
| 77 | 
            -
                        diff_data = ujson.load(f)
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                    diff_content = diff_data.get('diff', '')
         | 
| 80 | 
            -
                    original_files = diff_data.get('original files', [])
         | 
| 81 | 
            -
             | 
| 82 | 
            -
                    original_files_str = ujson.dumps(original_files, indent=4, ensure_ascii=False)
         | 
| 83 | 
            -
             | 
| 84 | 
            -
                    prompt = (
         | 
| 85 | 
            -
                        'Пожалуйста, проверь следующие изменения в коде на наличие очевидных проблем с качеством или безопасностью. '
         | 
| 86 | 
            -
                        'Предоставь краткий отчет в формате markdown:\n\n'
         | 
| 87 | 
            -
                        'DIFF:\n'
         | 
| 88 | 
            -
                        f'{diff_content}\n\n'
         | 
| 89 | 
            -
                        'ORIGINAL FILES:\n'
         | 
| 90 | 
            -
                        f'{original_files_str}'
         | 
| 91 | 
            -
                    )
         | 
| 92 | 
            -
                    try:
         | 
| 93 | 
            -
                        response = self._request_to_chat_agent(prompt=prompt)
         | 
| 94 | 
            -
                    except Exception as e:
         | 
| 95 | 
            -
                        logger.error('Error while send request: {}', e)
         | 
| 96 | 
            -
                        raise e
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                    with open(os.path.join(self.output_dir, self.response_file), 'w', encoding='utf-8') as f:
         | 
| 99 | 
            -
                        ujson.dump(response.json(), f, indent=4, ensure_ascii=False)
         | 
| 100 | 
            -
             | 
| 101 | 
            -
                    resp = response.json()
         | 
| 102 | 
            -
                    clear_response = resp['choices'][0]['message']['content']
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                    with open(os.path.join(self.output_dir, self.result_file), 'w', encoding='utf-8') as f:
         | 
| 105 | 
            -
                        f.write(clear_response)
         | 
| 106 | 
            -
             | 
| 107 | 
            -
                def start(self):
         | 
| 108 | 
            -
                    self.send_to_review()
         | 
| @@ -1,14 +0,0 @@ | |
| 1 | 
            -
            codespector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 2 | 
            -
            codespector/controller.py,sha256=z7TznOY6SBXCirBFpRO4bSh0_XY3gjMjUmnlFDkkc7Q,1211
         | 
| 3 | 
            -
            codespector/main.py,sha256=y_mij-Tj_LaFQ2DUn8wAKJLKYy60jvb162UBnI1QWGM,1950
         | 
| 4 | 
            -
            codespector/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 5 | 
            -
            codespector/clients/codestral.py,sha256=MV6_m9IMYzTHX5AS9UoPkhGUNHRoOXgIpTZ8uHfwZgQ,56
         | 
| 6 | 
            -
            codespector/local/__init__.py,sha256=spdqmAUYVjE-6RJUNUQ63ymwBY3svwC1gQp5QHAZdwg,35
         | 
| 7 | 
            -
            codespector/local/main.py,sha256=JQb8mBQEjxsjNnjpqCOP84ZF92T2DjTzCslRdHQLqlg,1136
         | 
| 8 | 
            -
            codespector/local/prepare.py,sha256=uqQft4RDH_5Rms3fGROc_fzCCRVcu-BxDzCAVF16buo,2821
         | 
| 9 | 
            -
            codespector/local/reviewer.py,sha256=76fd5_pTANnbpD-CgEhBboXbqvEFZGxR5a2WYpCA_8U,3594
         | 
| 10 | 
            -
            codespector-0.1.1.dist-info/METADATA,sha256=Y1r6wl48uZjliAMwO3tl1JYDvHwv0xPHlQ5pSCbX-dg,3160
         | 
| 11 | 
            -
            codespector-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
         | 
| 12 | 
            -
            codespector-0.1.1.dist-info/entry_points.txt,sha256=QlHn96KY8vzY1sOweKIuZOAQrSVse6h3v57vkwmHmJg,54
         | 
| 13 | 
            -
            codespector-0.1.1.dist-info/licenses/LICENSE,sha256=Eta34ENUL_dZWy-prVNucMkoA38WIqiO9pKTUYiuT_A,1070
         | 
| 14 | 
            -
            codespector-0.1.1.dist-info/RECORD,,
         | 
| 
            File without changes
         | 
| 
            File without changes
         | 
| 
            File without changes
         |