codespector 0.1.2__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.2.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.2.dist-info/RECORD +0 -14
- {codespector-0.1.2.dist-info → codespector-0.2.0.dist-info}/WHEEL +0 -0
- {codespector-0.1.2.dist-info → codespector-0.2.0.dist-info}/entry_points.txt +0 -0
- {codespector-0.1.2.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.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,,
|
File without changes
|
File without changes
|
File without changes
|