xai-review 0.11.0__py3-none-any.whl → 0.12.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.
Potentially problematic release.
This version of xai-review might be problematic. Click here for more details.
- ai_review/libs/config/prompt.py +1 -0
- ai_review/services/prompt/service.py +19 -18
- ai_review/services/prompt/tools.py +24 -0
- ai_review/tests/suites/services/prompt/test_service.py +33 -0
- ai_review/tests/suites/services/prompt/test_tools.py +72 -0
- {xai_review-0.11.0.dist-info → xai_review-0.12.0.dist-info}/METADATA +1 -1
- {xai_review-0.11.0.dist-info → xai_review-0.12.0.dist-info}/RECORD +11 -9
- {xai_review-0.11.0.dist-info → xai_review-0.12.0.dist-info}/WHEEL +0 -0
- {xai_review-0.11.0.dist-info → xai_review-0.12.0.dist-info}/entry_points.txt +0 -0
- {xai_review-0.11.0.dist-info → xai_review-0.12.0.dist-info}/licenses/LICENSE +0 -0
- {xai_review-0.11.0.dist-info → xai_review-0.12.0.dist-info}/top_level.txt +0 -0
ai_review/libs/config/prompt.py
CHANGED
|
@@ -8,6 +8,7 @@ from ai_review.libs.resources import load_resource
|
|
|
8
8
|
|
|
9
9
|
class PromptConfig(BaseModel):
|
|
10
10
|
context: dict[str, str] = Field(default_factory=dict)
|
|
11
|
+
normalize_prompts: bool = True
|
|
11
12
|
context_placeholder: str = "<<{value}>>"
|
|
12
13
|
inline_prompt_files: list[FilePath] | None = None
|
|
13
14
|
context_prompt_files: list[FilePath] | None = None
|
|
@@ -1,56 +1,57 @@
|
|
|
1
1
|
from ai_review.config import settings
|
|
2
2
|
from ai_review.services.diff.schema import DiffFileSchema
|
|
3
3
|
from ai_review.services.prompt.schema import PromptContextSchema
|
|
4
|
+
from ai_review.services.prompt.tools import normalize_prompt, format_file
|
|
4
5
|
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
class PromptService:
|
|
8
|
+
@classmethod
|
|
9
|
+
def prepare_prompt(cls, prompts: list[str], context: PromptContextSchema) -> str:
|
|
10
|
+
prompt = "\n\n".join(prompts)
|
|
11
|
+
prompt = context.apply_format(prompt)
|
|
8
12
|
|
|
13
|
+
if settings.prompt.normalize_prompts:
|
|
14
|
+
prompt = normalize_prompt(prompt)
|
|
15
|
+
|
|
16
|
+
return prompt
|
|
9
17
|
|
|
10
|
-
class PromptService:
|
|
11
18
|
@classmethod
|
|
12
19
|
def build_inline_request(cls, diff: DiffFileSchema, context: PromptContextSchema) -> str:
|
|
13
|
-
|
|
14
|
-
inline_prompts = context.apply_format(inline_prompts)
|
|
20
|
+
prompt = cls.prepare_prompt(settings.prompt.load_inline(), context)
|
|
15
21
|
return (
|
|
16
|
-
f"{
|
|
22
|
+
f"{prompt}\n\n"
|
|
17
23
|
f"## Diff\n\n"
|
|
18
24
|
f"{format_file(diff)}"
|
|
19
25
|
)
|
|
20
26
|
|
|
21
27
|
@classmethod
|
|
22
28
|
def build_summary_request(cls, diffs: list[DiffFileSchema], context: PromptContextSchema) -> str:
|
|
29
|
+
prompt = cls.prepare_prompt(settings.prompt.load_summary(), context)
|
|
23
30
|
changes = "\n\n".join(map(format_file, diffs))
|
|
24
|
-
summary_prompts = "\n\n".join(settings.prompt.load_summary())
|
|
25
|
-
summary_prompts = context.apply_format(summary_prompts)
|
|
26
31
|
return (
|
|
27
|
-
f"{
|
|
32
|
+
f"{prompt}\n\n"
|
|
28
33
|
f"## Changes\n\n"
|
|
29
34
|
f"{changes}\n"
|
|
30
35
|
)
|
|
31
36
|
|
|
32
37
|
@classmethod
|
|
33
38
|
def build_context_request(cls, diffs: list[DiffFileSchema], context: PromptContextSchema) -> str:
|
|
39
|
+
prompt = cls.prepare_prompt(settings.prompt.load_context(), context)
|
|
34
40
|
changes = "\n\n".join(map(format_file, diffs))
|
|
35
|
-
inline_prompts = "\n\n".join(settings.prompt.load_context())
|
|
36
|
-
inline_prompts = context.apply_format(inline_prompts)
|
|
37
41
|
return (
|
|
38
|
-
f"{
|
|
42
|
+
f"{prompt}\n\n"
|
|
39
43
|
f"## Diff\n\n"
|
|
40
44
|
f"{changes}\n"
|
|
41
45
|
)
|
|
42
46
|
|
|
43
47
|
@classmethod
|
|
44
48
|
def build_system_inline_request(cls, context: PromptContextSchema) -> str:
|
|
45
|
-
|
|
46
|
-
return context.apply_format(prompt)
|
|
49
|
+
return cls.prepare_prompt(settings.prompt.load_system_inline(), context)
|
|
47
50
|
|
|
48
51
|
@classmethod
|
|
49
52
|
def build_system_context_request(cls, context: PromptContextSchema) -> str:
|
|
50
|
-
|
|
51
|
-
return context.apply_format(prompt)
|
|
53
|
+
return cls.prepare_prompt(settings.prompt.load_system_context(), context)
|
|
52
54
|
|
|
53
55
|
@classmethod
|
|
54
56
|
def build_system_summary_request(cls, context: PromptContextSchema) -> str:
|
|
55
|
-
|
|
56
|
-
return context.apply_format(prompt)
|
|
57
|
+
return cls.prepare_prompt(settings.prompt.load_system_summary(), context)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from ai_review.libs.logger import get_logger
|
|
4
|
+
from ai_review.services.diff.schema import DiffFileSchema
|
|
5
|
+
|
|
6
|
+
logger = get_logger("PROMPT_TOOLS")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def format_file(diff: DiffFileSchema) -> str:
|
|
10
|
+
return f"# File: {diff.file}\n{diff.diff}\n"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def normalize_prompt(text: str) -> str:
|
|
14
|
+
tails_stripped = [re.sub(r"[ \t]+$", "", line) for line in text.splitlines()]
|
|
15
|
+
text = "\n".join(tails_stripped)
|
|
16
|
+
|
|
17
|
+
text = re.sub(r"\n{3,}", "\n\n", text)
|
|
18
|
+
|
|
19
|
+
result = text.strip()
|
|
20
|
+
if len(text) > len(result):
|
|
21
|
+
logger.info(f"Prompt has been normalized from {len(text)} to {len(result)}")
|
|
22
|
+
return result
|
|
23
|
+
|
|
24
|
+
return text
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
|
+
from ai_review.config import settings
|
|
3
4
|
from ai_review.libs.config.prompt import PromptConfig
|
|
4
5
|
from ai_review.services.diff.schema import DiffFileSchema
|
|
5
6
|
from ai_review.services.prompt.schema import PromptContextSchema
|
|
@@ -134,3 +135,35 @@ def test_diff_placeholders_are_not_replaced(dummy_context: PromptContextSchema)
|
|
|
134
135
|
|
|
135
136
|
assert "<<merge_request_title>>" in result
|
|
136
137
|
assert "Fix login bug" not in result
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_prepare_prompt_basic_substitution(dummy_context: PromptContextSchema) -> None:
|
|
141
|
+
prompts = ["Hello", "MR title: <<merge_request_title>>"]
|
|
142
|
+
result = PromptService.prepare_prompt(prompts, dummy_context)
|
|
143
|
+
assert "Hello" in result
|
|
144
|
+
assert "MR title: Fix login bug" in result
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_prepare_prompt_applies_normalization(
|
|
148
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
149
|
+
dummy_context: PromptContextSchema
|
|
150
|
+
) -> None:
|
|
151
|
+
monkeypatch.setattr(settings.prompt, "normalize_prompts", True)
|
|
152
|
+
prompts = ["Line with space ", "", "", "Next line"]
|
|
153
|
+
result = PromptService.prepare_prompt(prompts, dummy_context)
|
|
154
|
+
|
|
155
|
+
assert "Line with space" in result
|
|
156
|
+
assert "Next line" in result
|
|
157
|
+
assert "\n\n\n" not in result
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_prepare_prompt_skips_normalization(
|
|
161
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
162
|
+
dummy_context: PromptContextSchema
|
|
163
|
+
) -> None:
|
|
164
|
+
monkeypatch.setattr(settings.prompt, "normalize_prompts", False)
|
|
165
|
+
prompts = ["Line with space ", "", "", "Next line"]
|
|
166
|
+
result = PromptService.prepare_prompt(prompts, dummy_context)
|
|
167
|
+
|
|
168
|
+
assert "Line with space " in result
|
|
169
|
+
assert "\n\n\n" in result
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from ai_review.services.diff.schema import DiffFileSchema
|
|
2
|
+
from ai_review.services.prompt.tools import format_file, normalize_prompt
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_format_file_basic():
|
|
6
|
+
diff = DiffFileSchema(file="main.py", diff="+ print('hello')")
|
|
7
|
+
result = format_file(diff)
|
|
8
|
+
assert result == "# File: main.py\n+ print('hello')\n"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_format_file_empty_diff():
|
|
12
|
+
diff = DiffFileSchema(file="empty.py", diff="")
|
|
13
|
+
result = format_file(diff)
|
|
14
|
+
assert result == "# File: empty.py\n\n"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_format_file_multiline_diff():
|
|
18
|
+
diff = DiffFileSchema(
|
|
19
|
+
file="utils/helpers.py",
|
|
20
|
+
diff="- old line\n+ new line\n+ another line"
|
|
21
|
+
)
|
|
22
|
+
result = format_file(diff)
|
|
23
|
+
expected = (
|
|
24
|
+
"# File: utils/helpers.py\n"
|
|
25
|
+
"- old line\n"
|
|
26
|
+
"+ new line\n"
|
|
27
|
+
"+ another line\n"
|
|
28
|
+
)
|
|
29
|
+
assert result == expected
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_format_file_filename_with_path():
|
|
33
|
+
diff = DiffFileSchema(file="src/app/models/user.py", diff="+ class User:")
|
|
34
|
+
result = format_file(diff)
|
|
35
|
+
assert result.startswith("# File: src/app/models/user.py\n")
|
|
36
|
+
assert result.endswith("+ class User:\n")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_trailing_spaces_are_removed():
|
|
40
|
+
text = "hello \nworld\t\t"
|
|
41
|
+
result = normalize_prompt(text)
|
|
42
|
+
assert result == "hello\nworld"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_multiple_empty_lines_collapsed():
|
|
46
|
+
text = "line1\n\n\n\nline2"
|
|
47
|
+
result = normalize_prompt(text)
|
|
48
|
+
assert result == "line1\n\nline2"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_leading_and_trailing_whitespace_removed():
|
|
52
|
+
text = "\n\n hello\nworld \n\n"
|
|
53
|
+
result = normalize_prompt(text)
|
|
54
|
+
assert result == "hello\nworld"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_internal_spaces_preserved():
|
|
58
|
+
text = "foo bar\nbaz\t\tqux"
|
|
59
|
+
result = normalize_prompt(text)
|
|
60
|
+
assert result == "foo bar\nbaz\t\tqux"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_only_whitespace_string():
|
|
64
|
+
text = " \n \n"
|
|
65
|
+
result = normalize_prompt(text)
|
|
66
|
+
assert result == ""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_no_changes_when_already_clean():
|
|
70
|
+
text = "line1\nline2"
|
|
71
|
+
result = normalize_prompt(text)
|
|
72
|
+
assert result == text
|
|
@@ -41,7 +41,7 @@ ai_review/libs/config/http.py,sha256=QsIj0yH1IYELOFBQ5AoqPZT0kGIIrQ19cxk1ozPRhLE
|
|
|
41
41
|
ai_review/libs/config/llm.py,sha256=cK-e4NCQxnnixLATCsO8-r5k3zUWz1N0BdPCoqerORM,1824
|
|
42
42
|
ai_review/libs/config/logger.py,sha256=oPmjpjf6EZwW7CgOjT8mOQdGnT98CLwXepiGB_ajZvU,384
|
|
43
43
|
ai_review/libs/config/openai.py,sha256=vOYqhUq0ceEuNdQrQaHq44lVS5M648mB61Zc4YlfJVw,271
|
|
44
|
-
ai_review/libs/config/prompt.py,sha256=
|
|
44
|
+
ai_review/libs/config/prompt.py,sha256=8aO5WNnhVhQcpWzWxqzb9lq6PzormaJazVwPHuf_ia8,4469
|
|
45
45
|
ai_review/libs/config/review.py,sha256=LEZni68iH_0m4URPfN0d3F6yrrK7KSn-BwXf-7w2al8,1058
|
|
46
46
|
ai_review/libs/config/vcs.py,sha256=FduvJJkGObh2LgTSapaMB6FABIvjX7E63yTwZF4p8CU,517
|
|
47
47
|
ai_review/libs/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -98,7 +98,8 @@ ai_review/services/llm/openai/client.py,sha256=WhMXNfH_G1NTlFkdRK5sgYvrCIE5ZQNfP
|
|
|
98
98
|
ai_review/services/prompt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
99
99
|
ai_review/services/prompt/adapter.py,sha256=humGHLRVBu0JspeULgYHCs782BAy4YYKSf5yaG8aF24,1003
|
|
100
100
|
ai_review/services/prompt/schema.py,sha256=erAecUYzOWyZfixt-pjmPSnvcMDh5DajMd1b7_SPm_o,2052
|
|
101
|
-
ai_review/services/prompt/service.py,sha256=
|
|
101
|
+
ai_review/services/prompt/service.py,sha256=D1PR2HC4cgrEND6mAhU5EPRAtp4mgEkOLEyD51WBc0g,2129
|
|
102
|
+
ai_review/services/prompt/tools.py,sha256=-gS74mVM3OZPKWQkY9_QfStkfxaUfssDbJ3Bdi4AQ74,636
|
|
102
103
|
ai_review/services/review/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
103
104
|
ai_review/services/review/service.py,sha256=8YhRFqhZAk2pAnkDaytKSCENlOeOti1brAJq3R9tVMY,8394
|
|
104
105
|
ai_review/services/review/inline/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -147,7 +148,8 @@ ai_review/tests/suites/services/diff/test_service.py,sha256=iFkGX9Vj2X44JU3eFsoH
|
|
|
147
148
|
ai_review/tests/suites/services/diff/test_tools.py,sha256=HBQ3eCn-kLeb_k5iTgyr09x0VwLYSegSbxm0Qk9ZrCc,3543
|
|
148
149
|
ai_review/tests/suites/services/prompt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
149
150
|
ai_review/tests/suites/services/prompt/test_schema.py,sha256=XkJk4N9ovgod7G3i6oZwRBjpd71sv0vtVDJhSOfIwGA,2660
|
|
150
|
-
ai_review/tests/suites/services/prompt/test_service.py,sha256=
|
|
151
|
+
ai_review/tests/suites/services/prompt/test_service.py,sha256=plJ8xDnBifCrLtHJO00cdl11U1EsqSw7lBrEGxu0AIw,6752
|
|
152
|
+
ai_review/tests/suites/services/prompt/test_tools.py,sha256=_yNZoBATvPU5enWNIopbjY8lVVjfaB_46kNIKODhCW4,1981
|
|
151
153
|
ai_review/tests/suites/services/review/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
152
154
|
ai_review/tests/suites/services/review/inline/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
153
155
|
ai_review/tests/suites/services/review/inline/test_schema.py,sha256=tIz-1UA_GgwcdsyUqgrodiiVVmd_jhoOVmtEwzRVWiY,2474
|
|
@@ -157,9 +159,9 @@ ai_review/tests/suites/services/review/policy/test_service.py,sha256=kRWT550OjWY
|
|
|
157
159
|
ai_review/tests/suites/services/review/summary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
158
160
|
ai_review/tests/suites/services/review/summary/test_schema.py,sha256=xSoydvABZldHaVDa0OBFvYrj8wMuZqUDN3MO-XdvxOI,819
|
|
159
161
|
ai_review/tests/suites/services/review/summary/test_service.py,sha256=8UMvi_NL9frm280vD6Q1NCDrdI7K8YbXzoViIus-I2g,541
|
|
160
|
-
xai_review-0.
|
|
161
|
-
xai_review-0.
|
|
162
|
-
xai_review-0.
|
|
163
|
-
xai_review-0.
|
|
164
|
-
xai_review-0.
|
|
165
|
-
xai_review-0.
|
|
162
|
+
xai_review-0.12.0.dist-info/licenses/LICENSE,sha256=p-v8m7Kmz4KKc7PcvsGiGEmCw9AiSXY4_ylOPy_u--Y,11343
|
|
163
|
+
xai_review-0.12.0.dist-info/METADATA,sha256=LCwEqar1dH6shbzNbIh4hZkuLcz0pOrhWz27Rta769I,9618
|
|
164
|
+
xai_review-0.12.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
165
|
+
xai_review-0.12.0.dist-info/entry_points.txt,sha256=JyC5URanMi5io5P_PXQf7H_I1OGIpk5cZQhaPQ0g4Zs,53
|
|
166
|
+
xai_review-0.12.0.dist-info/top_level.txt,sha256=sTsZbfzLoqvRZKdKa-BcxWvjlHdrpbeJ6DrGY0EuR0E,10
|
|
167
|
+
xai_review-0.12.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|