codespector 0.1.2__tar.gz → 1.0.0__tar.gz

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.
Files changed (31) hide show
  1. {codespector-0.1.2 → codespector-1.0.0}/PKG-INFO +25 -18
  2. codespector-1.0.0/README.md +85 -0
  3. codespector-1.0.0/cicd_examples/.gitlab-ci.yml +41 -0
  4. codespector-1.0.0/codespector/base.py +7 -0
  5. codespector-1.0.0/codespector/codespector.py +89 -0
  6. codespector-1.0.0/codespector/errors.py +14 -0
  7. codespector-0.1.2/codespector/local/prepare.py → codespector-1.0.0/codespector/local.py +15 -13
  8. {codespector-0.1.2 → codespector-1.0.0}/codespector/main.py +26 -24
  9. codespector-1.0.0/codespector/reviewer.py +84 -0
  10. codespector-1.0.0/codespector/types.py +50 -0
  11. {codespector-0.1.2 → codespector-1.0.0}/pyproject.toml +1 -2
  12. codespector-1.0.0/tests/unit/test_codespector.py +78 -0
  13. {codespector-0.1.2 → codespector-1.0.0}/uv.lock +83 -27
  14. codespector-0.1.2/README.md +0 -77
  15. codespector-0.1.2/codespector/clients/codestral.py +0 -5
  16. codespector-0.1.2/codespector/controller.py +0 -44
  17. codespector-0.1.2/codespector/local/__init__.py +0 -1
  18. codespector-0.1.2/codespector/local/main.py +0 -35
  19. codespector-0.1.2/codespector/local/reviewer.py +0 -108
  20. codespector-0.1.2/tests/unit/__init__.py +0 -0
  21. codespector-0.1.2/tests/unit/test_prepare.py +0 -26
  22. codespector-0.1.2/tests/unit/test_reviewer.py +0 -74
  23. {codespector-0.1.2 → codespector-1.0.0}/.github/workflows/build-publish.yml +0 -0
  24. {codespector-0.1.2 → codespector-1.0.0}/.github/workflows/tests.yml +0 -0
  25. {codespector-0.1.2 → codespector-1.0.0}/.gitignore +0 -0
  26. {codespector-0.1.2 → codespector-1.0.0}/LICENSE +0 -0
  27. {codespector-0.1.2 → codespector-1.0.0}/Makefile +0 -0
  28. {codespector-0.1.2 → codespector-1.0.0}/codespector/__init__.py +0 -0
  29. {codespector-0.1.2/codespector/clients → codespector-1.0.0/tests}/__init__.py +0 -0
  30. {codespector-0.1.2 → codespector-1.0.0}/tests/conftest.py +0 -0
  31. {codespector-0.1.2/tests → codespector-1.0.0/tests/unit}/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codespector
3
- Version: 0.1.2
3
+ Version: 1.0.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
@@ -51,18 +50,23 @@ You can use the `codespector` command to start a code review. Below are the avai
51
50
  Usage: codespector [OPTIONS]
52
51
 
53
52
  Options:
54
- --system-content TEXT Content which used in system field for agent
55
- [default: Ты код ревьювер. Отвечай на русском языке.]
56
- --output-dir TEXT Select the output directory [default: codespector]
57
- -b, --compare-branch TEXT Select the branch to compare the current one with
58
- [default: develop]
59
- --chat-agent [codestral|chatgpt]
60
- Choose the chat agent to use [default: codestral]
61
- --chat-model TEXT Choose the chat model to use
62
- --chat-token TEXT Chat agent token
63
- --mode [local] Choose the mode of the application [default: local]
64
- --version Show the version and exit.
65
- --help Show this message and exit.
53
+ --chat-token TEXT Chat agent token [env var: CODESPECTOR_CHAT_TOKEN]
54
+ --chat-model TEXT Choose the chat model to use [env var: CODESPECTOR_CHAT_MODEL]
55
+ --chat-agent TEXT Choose the chat agent to use (codestral, chatgpt, deepseek)
56
+ or set your own [env var: CODESPECTOR_CHAT_AGENT]
57
+ -b, --compare-branch TEXT Select the branch to compare the current one with
58
+ --output-dir TEXT Select the output directory [default: codespector]
59
+ [env var: CODESPECTOR_OUTPUT_DIR]
60
+ --system-content TEXT Content which used in system field for agent
61
+ [env var: CODESPECTOR_SYSTEM_CONTENT]
62
+ --prompt-content TEXT Prompt content which included to review prompt
63
+ [env var: CODESPECTOR_PROMPT_CONTENT]
64
+ --result-file TEXT Set file for saving the result
65
+ [env var: CODESPECTOR_RESULT_FILE]
66
+ --exclude-file-ext LIST Exclude file extensions from the review
67
+ [env var: CODESPECTOR_EXCLUDE_FILE_EXT]
68
+ --version Show the version and exit.
69
+ --help Show this message and exit.
66
70
  ```
67
71
 
68
72
  ### Example
@@ -70,7 +74,7 @@ Options:
70
74
  To run a code review, use the following command:
71
75
 
72
76
  ```sh
73
- codespector --chat-token YOUR_CHAT_TOKEN --chat-agent codestral --compare-branch develop
77
+ codespector --chat-token YOUR_CHAT_TOKEN --chat-agent codestral --compare-branch develop --result-file result.md --system-content "system content" --prompt-content "prompt content"
74
78
  ```
75
79
 
76
80
  ## Configuration
@@ -78,11 +82,14 @@ codespector --chat-token YOUR_CHAT_TOKEN --chat-agent codestral --compare-branch
78
82
  You can also configure CodeSpector using environment variables. Create a `.env` file in the root directory of your project with the following content:
79
83
 
80
84
  ```
81
- CODESPECTOR_SYSTEM_CONTENT=Ты код ревьювер. Отвечай на русском языке.
85
+ CODESPECTOR_CHAT_TOKEN=your_token
82
86
  CODESPECTOR_OUTPUT_DIR=codespector
87
+ CODESPECTOR_SYSTEM_CONTENT="Ты код ревьювер. Отвечай на русском"
88
+ CODESPECTOR_PROMPT_CONTENT="Оцени код на безопасноть, соблюдение лучших техник"
89
+ CODESPECTOR_RESULT_FILE="result.md"
83
90
  CODESPECTOR_CHAT_AGENT=codestral
84
- CODESPECTOR_CHAT_MODEL=codestral-latest
85
- CODESPECTOR_CHAT_TOKEN=YOUR_CHAT_TOKEN
91
+ CODESPECTOR_CHAT_MODEL=your_model
92
+ CODESPECTOR_EXCLUDE_FILE_EXT=.pyc,.pyo
86
93
  ```
87
94
 
88
95
  ## Makefile Commands
@@ -0,0 +1,85 @@
1
+ # CodeSpector
2
+
3
+ CodeSpector is a Python package designed to review code changes for quality and security issues using AI chat agents. It supports different chat agents like Codestral and ChatGPT.
4
+
5
+ ## Features
6
+
7
+ - Automated code review using AI chat agents.
8
+ - Supports multiple chat agents and models.
9
+ - Generates detailed review reports in markdown format.
10
+ - Configurable via environment variables and command-line options.
11
+
12
+ ## Installation
13
+
14
+ To install the package, use the following command:
15
+
16
+ ```sh
17
+ pip install codespector
18
+ ```
19
+
20
+ ```sh
21
+ uv add codespector
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Command-Line Interface
27
+
28
+ You can use the `codespector` command to start a code review. Below are the available options:
29
+
30
+ ```sh
31
+ Usage: codespector [OPTIONS]
32
+
33
+ Options:
34
+ --chat-token TEXT Chat agent token [env var: CODESPECTOR_CHAT_TOKEN]
35
+ --chat-model TEXT Choose the chat model to use [env var: CODESPECTOR_CHAT_MODEL]
36
+ --chat-agent TEXT Choose the chat agent to use (codestral, chatgpt, deepseek)
37
+ or set your own [env var: CODESPECTOR_CHAT_AGENT]
38
+ -b, --compare-branch TEXT Select the branch to compare the current one with
39
+ --output-dir TEXT Select the output directory [default: codespector]
40
+ [env var: CODESPECTOR_OUTPUT_DIR]
41
+ --system-content TEXT Content which used in system field for agent
42
+ [env var: CODESPECTOR_SYSTEM_CONTENT]
43
+ --prompt-content TEXT Prompt content which included to review prompt
44
+ [env var: CODESPECTOR_PROMPT_CONTENT]
45
+ --result-file TEXT Set file for saving the result
46
+ [env var: CODESPECTOR_RESULT_FILE]
47
+ --exclude-file-ext LIST Exclude file extensions from the review
48
+ [env var: CODESPECTOR_EXCLUDE_FILE_EXT]
49
+ --version Show the version and exit.
50
+ --help Show this message and exit.
51
+ ```
52
+
53
+ ### Example
54
+
55
+ To run a code review, use the following command:
56
+
57
+ ```sh
58
+ codespector --chat-token YOUR_CHAT_TOKEN --chat-agent codestral --compare-branch develop --result-file result.md --system-content "system content" --prompt-content "prompt content"
59
+ ```
60
+
61
+ ## Configuration
62
+
63
+ You can also configure CodeSpector using environment variables. Create a `.env` file in the root directory of your project with the following content:
64
+
65
+ ```
66
+ CODESPECTOR_CHAT_TOKEN=your_token
67
+ CODESPECTOR_OUTPUT_DIR=codespector
68
+ CODESPECTOR_SYSTEM_CONTENT="Ты код ревьювер. Отвечай на русском"
69
+ CODESPECTOR_PROMPT_CONTENT="Оцени код на безопасноть, соблюдение лучших техник"
70
+ CODESPECTOR_RESULT_FILE="result.md"
71
+ CODESPECTOR_CHAT_AGENT=codestral
72
+ CODESPECTOR_CHAT_MODEL=your_model
73
+ CODESPECTOR_EXCLUDE_FILE_EXT=.pyc,.pyo
74
+ ```
75
+
76
+ ## Makefile Commands
77
+
78
+ - `lint`: Run linting and formatting checks.
79
+ - `format`: Format the code.
80
+ - `fix`: Fix linting issues and format the code.
81
+ - `test`: Run the tests.
82
+
83
+ ## License
84
+
85
+ This project is licensed under the MIT License. See the `LICENSE` file for more details.
@@ -0,0 +1,41 @@
1
+ workflow:
2
+ rules:
3
+ - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
4
+
5
+ variables:
6
+ GIT_DEPTH: 0
7
+ CODESPECTOR_RESULT_FILE: "codespector/result.md"
8
+ CODESPECTOR_COMBINED_FILE: "codespector/combined.json"
9
+
10
+ stages:
11
+ - codespector
12
+
13
+ codespector-job:
14
+ stage: codespector
15
+ image: python:3.12
16
+ script:
17
+ - apt-get update && apt-get install -y jq
18
+ - git fetch --no-tags origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
19
+ - pip install codespector
20
+ - |
21
+ codespector
22
+ --chat-token $CODESPECTOR_CHAT_TOKEN
23
+ --chat-agent $CODESPECTOR_CHAT_AGENT
24
+ --compare-branch origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD
25
+ --result-file $CODESPECTOR_RESULT_FILE
26
+ --system-content $CODESCPECTOR_SYSTEM_CONTENT
27
+ --prompt-content $CODESPECTOR_PROMPT_CONTENT
28
+
29
+ - |
30
+ COMMENT_CONTENT=$(cat ${CODESPECTOR_RESULT_FILE} | jq -Rs .)
31
+ echo "Posting comment to MR..."
32
+ curl -X POST \
33
+ --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
34
+ --header "Content-Type: application/json" \
35
+ --data "{\"body\": ${COMMENT_CONTENT}}" \
36
+ "$CI_API_V4_URL/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes"
37
+ artifacts:
38
+ paths:
39
+ - ${CODESPECTOR_RESULT_FILE}
40
+ - ${CODESPECTOR_COMBINED_FILE}
41
+ expire_in: 10 minutes
@@ -0,0 +1,7 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class BasePipe(ABC):
5
+ @abstractmethod
6
+ def start(self, *args, **kwargs) -> None:
7
+ raise NotImplementedError
@@ -0,0 +1,89 @@
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
+ exclude_file_ext: list[str] | None = None,
22
+ ):
23
+ self.chat_token = chat_token
24
+ self.chat_agent = chat_agent
25
+ self.compare_branch = compare_branch
26
+ self.output_dir = output_dir
27
+ self.system_content = system_content
28
+ self.prompt_content = prompt_content
29
+ self.data_preparer = data_preparer
30
+ self.reviewer = reviewer
31
+ self.pipeline = pipeline
32
+ self.result_file = result_file
33
+ self.chat_model = chat_model
34
+ self.exclude_file_exc: list[str] | None = (None,)
35
+
36
+ @classmethod
37
+ def create(
38
+ cls,
39
+ chat_token: str,
40
+ chat_agent: str,
41
+ compare_branch: str,
42
+ system_content: str,
43
+ prompt_content: str,
44
+ result_file: str,
45
+ output_dir: str,
46
+ chat_model: str | None = None,
47
+ exclude_file_ext: list[str] | None = None,
48
+ ) -> 'CodeSpector':
49
+ agent_info = AgentInfo.create(
50
+ chat_agent=chat_agent,
51
+ chat_token=chat_token,
52
+ chat_model=chat_model,
53
+ )
54
+ data_preparer = CodeSpectorDataPreparer(
55
+ output_dir=output_dir,
56
+ compare_branch=compare_branch,
57
+ exclude_file_ext=exclude_file_ext,
58
+ )
59
+ reviewer = CodeSpectorReviewer(
60
+ diff_file=data_preparer.combined_file,
61
+ chat_token=chat_token,
62
+ chat_agent=chat_agent,
63
+ system_content=system_content,
64
+ output_dir=output_dir,
65
+ chat_model=chat_model,
66
+ agent_info=agent_info,
67
+ prompt_content=prompt_content,
68
+ result_file=result_file,
69
+ )
70
+ pipeline = [data_preparer, reviewer]
71
+
72
+ return CodeSpector(
73
+ chat_token=chat_token,
74
+ chat_agent=chat_agent,
75
+ chat_model=chat_model,
76
+ compare_branch=compare_branch,
77
+ system_content=system_content,
78
+ prompt_content=prompt_content,
79
+ data_preparer=data_preparer,
80
+ reviewer=reviewer,
81
+ pipeline=pipeline,
82
+ output_dir=output_dir,
83
+ result_file=result_file,
84
+ exclude_file_ext=exclude_file_ext,
85
+ )
86
+
87
+ def review(self):
88
+ for pipe in self.pipeline:
89
+ pipe.start()
@@ -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,16 +1,20 @@
1
- import ujson
1
+ import json
2
2
  import subprocess
3
3
  import os
4
4
 
5
+ from codespector.base import BasePipe
5
6
 
6
- class CodeSpectorDataPreparer:
7
+
8
+ class CodeSpectorDataPreparer(BasePipe):
7
9
  def __init__(
8
10
  self,
9
11
  output_dir: str,
10
12
  compare_branch: str,
13
+ exclude_file_ext: list[str] | None = None,
11
14
  ):
12
15
  self.output_dir = output_dir
13
16
  self.compare_branch = compare_branch
17
+ self.exclude_file_ext = exclude_file_ext
14
18
 
15
19
  self.original_files_tmp = 'original_files_tmp.json'
16
20
  self.code_changes_only = 'code_changes_only.txt'
@@ -38,18 +42,18 @@ class CodeSpectorDataPreparer:
38
42
  diff_json = {'diff': '\n'.join(filtered_diff)}
39
43
  diff_filepath = os.path.join(self.output_dir, self.diff_file)
40
44
  with open(diff_filepath, 'w', encoding='utf-8') as f:
41
- ujson.dump(diff_json, f, indent=4, ensure_ascii=False)
45
+ json.dump(diff_json, f, indent=4, ensure_ascii=False)
42
46
 
43
47
  with open(os.path.join(self.output_dir, self.original_files_tmp), 'r', encoding='utf-8') as f:
44
- original_files_data = ujson.load(f)
48
+ original_files_data = json.load(f)
45
49
 
46
50
  with open(diff_filepath, 'r', encoding='utf-8') as f:
47
- diff_data = ujson.load(f)
51
+ diff_data = json.load(f)
48
52
 
49
53
  combined_data = {**original_files_data, **diff_data}
50
54
 
51
55
  with open(os.path.join(self.output_dir, self.combined_file), 'w', encoding='utf-8') as f:
52
- ujson.dump(combined_data, f, indent=4, ensure_ascii=False)
56
+ json.dump(combined_data, f, indent=4, ensure_ascii=False)
53
57
 
54
58
  def _prepare_name_only_file(self):
55
59
  changed_files = subprocess.run(
@@ -59,8 +63,9 @@ class CodeSpectorDataPreparer:
59
63
  result = {'original files': []}
60
64
 
61
65
  for file in changed_files:
62
- if not file.endswith('.py'):
63
- continue
66
+ if self.exclude_file_ext:
67
+ if file.endswith(tuple(self.exclude_file_ext)):
68
+ continue
64
69
 
65
70
  if os.path.isfile(file):
66
71
  with open(file, 'r', encoding='utf-8') as f:
@@ -70,12 +75,9 @@ class CodeSpectorDataPreparer:
70
75
  filepath = os.path.join(self.output_dir, self.original_files_tmp)
71
76
 
72
77
  with open(filepath, 'w', encoding='utf-8') as f:
73
- ujson.dump(result, f, indent=4, ensure_ascii=False)
78
+ json.dump(result, f, indent=4, ensure_ascii=False)
74
79
 
75
- def prepare_data(self) -> str:
80
+ def start(self):
76
81
  self._prepare_dir()
77
82
  self._prepare_name_only_file()
78
83
  self._prepare_diff_file()
79
-
80
- def start(self):
81
- self.prepare_data()
@@ -3,8 +3,7 @@ from pathlib import Path
3
3
  import click
4
4
  from environs import Env
5
5
 
6
- from .controller import CodeSpectorController
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,30 @@ env = Env()
12
11
  env.read_env(path=str(BASE_PATH / '.env'))
13
12
 
14
13
 
14
+ @click.option(
15
+ '--exclude-file-ext',
16
+ type=list,
17
+ envvar='CODESPECTOR_EXCLUDE_FILE_EXT',
18
+ help='Exclude file extensions from the review',
19
+ show_envvar=True,
20
+ )
21
+ @click.option(
22
+ '--result-file',
23
+ type=str,
24
+ help='Set file for saving the result',
25
+ envvar='CODESPECTOR_RESULT_FILE',
26
+ show_envvar=True,
27
+ )
28
+ @click.option(
29
+ '--prompt-content',
30
+ type=str,
31
+ help='Prompt content which included to review prompt',
32
+ envvar='CODESPECTOR_PROMPT_CONTENT',
33
+ show_envvar=True,
34
+ )
15
35
  @click.option(
16
36
  '--system-content',
17
37
  type=str,
18
- default='Ты код ревьювер. Отвечай на русском языке.',
19
38
  envvar='CODESPECTOR_SYSTEM_CONTENT',
20
39
  show_envvar=True,
21
40
  help='Content which used in system field for agent',
@@ -23,8 +42,8 @@ env.read_env(path=str(BASE_PATH / '.env'))
23
42
  @click.option(
24
43
  '--output-dir',
25
44
  type=str,
26
- default='codespector',
27
45
  envvar='CODESPECTOR_OUTPUT_DIR',
46
+ default='codespector',
28
47
  show_envvar=True,
29
48
  help='Select the output directory',
30
49
  )
@@ -32,16 +51,13 @@ env.read_env(path=str(BASE_PATH / '.env'))
32
51
  '-b',
33
52
  '--compare-branch',
34
53
  type=str,
35
- default='develop',
36
54
  help='Select the branch to compare the current one with',
37
55
  )
38
56
  @click.option(
39
57
  '--chat-agent',
40
- type=click.Choice(['codestral', 'chatgpt'], case_sensitive=False),
41
58
  envvar='CODESPECTOR_CHAT_AGENT',
42
59
  show_envvar=True,
43
- default='codestral',
44
- help='Choose the chat agent to use',
60
+ help='Choose the chat agent to use you can use one from [codestral, chatgpt, deepseek]. Or set yours chat agent',
45
61
  )
46
62
  @click.option(
47
63
  '--chat-model',
@@ -56,25 +72,11 @@ env.read_env(path=str(BASE_PATH / '.env'))
56
72
  envvar='CODESPECTOR_CHAT_TOKEN',
57
73
  show_envvar=True,
58
74
  )
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
75
  @click.version_option(message='%(version)s')
66
76
  @click.command()
67
77
  def main(*args, **kwargs):
68
- return start(*args, **kwargs)
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)
78
+ codespector = CodeSpector.create(*args, **kwargs)
79
+ codespector.review()
78
80
 
79
81
 
80
82
  if __name__ == '__main__':
@@ -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()
@@ -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
  [project]
2
2
  name = "codespector"
3
- version = "0.1.2"
3
+ version = "1.0.0"
4
4
  authors = [
5
5
  { name = "vtitov", email = "v.v.titov94@gmail.com" }
6
6
  ]
@@ -12,7 +12,6 @@ dependencies = [
12
12
  "environs>=14.1.1",
13
13
  "loguru>=0.7.3",
14
14
  "requests>=2.32.3",
15
- "ujson>=5.10.0",
16
15
  ]
17
16
 
18
17
  classifiers = [