codespector 0.1.2__py3-none-any.whl → 1.0.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 ADDED
@@ -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()
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,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()
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 .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()
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.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,13 @@
1
+ codespector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ codespector/base.py,sha256=uPoJ8h0Dlna-eUXyyD9s7uaQ4z0pS6qPdQMeu10muEk,159
3
+ codespector/codespector.py,sha256=XHUbdWOZB0HSpXLdpDDFDIse9tiwVJ-gSyOPV0SCHcw,2842
4
+ codespector/errors.py,sha256=Z5W6i7iG_iBj_IfdSZ_f9jij0oPxTZ2Cxs87kzk3oyU,377
5
+ codespector/local.py,sha256=neODRe9DwQ_ghXccXVc-A-hdVA6Tz7EtVcmCwUh4HHs,2965
6
+ codespector/main.py,sha256=NoXN8nraW6_EtDK1oOx63rojs_fuSNeM2VX-rsSyg7o,1882
7
+ codespector/reviewer.py,sha256=WoKRO-bFFLKNUiWB_TWoK0rRjenHkJ5fej_lhcj_e2Q,2740
8
+ codespector/types.py,sha256=i5PFazyUc5rVq5Fhn4_FsG7R1l617vyjByvnixDUw1Y,1255
9
+ codespector-1.0.0.dist-info/METADATA,sha256=5gkMmkUJHFw7yjAl2xA7YXLs1sAqiBqOg9ciqT3-t_o,3791
10
+ codespector-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
+ codespector-1.0.0.dist-info/entry_points.txt,sha256=QlHn96KY8vzY1sOweKIuZOAQrSVse6h3v57vkwmHmJg,54
12
+ codespector-1.0.0.dist-info/licenses/LICENSE,sha256=Eta34ENUL_dZWy-prVNucMkoA38WIqiO9pKTUYiuT_A,1070
13
+ codespector-1.0.0.dist-info/RECORD,,
File without changes
@@ -1,5 +0,0 @@
1
- import ujson
2
-
3
-
4
- def chat_completion(diff: str):
5
- pass
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)
@@ -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()
@@ -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.md'
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=bsbdwXMhkyXMfIp55u5a8JRyndO-kjqzlQBVeoLdgMQ,3593
10
- codespector-0.1.2.dist-info/METADATA,sha256=BWtpEmWdlrwRRYPVeig0RFtYTNx1dDXqmeGMGRplH2w,3160
11
- codespector-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
- codespector-0.1.2.dist-info/entry_points.txt,sha256=QlHn96KY8vzY1sOweKIuZOAQrSVse6h3v57vkwmHmJg,54
13
- codespector-0.1.2.dist-info/licenses/LICENSE,sha256=Eta34ENUL_dZWy-prVNucMkoA38WIqiO9pKTUYiuT_A,1070
14
- codespector-0.1.2.dist-info/RECORD,,