xai-review 0.5.0__py3-none-any.whl → 0.6.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.

@@ -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
+ context_placeholder: str = "<<{value}>>"
11
12
  inline_prompt_files: list[FilePath] | None = None
12
13
  context_prompt_files: list[FilePath] | None = None
13
14
  summary_prompt_files: list[FilePath] | None = None
File without changes
@@ -0,0 +1,13 @@
1
+ import re
2
+ from typing import Mapping
3
+
4
+
5
+ def render_template(text: str, values: Mapping[str, str], placeholder: str = "<<{value}>>") -> str:
6
+ regex_pattern = re.escape(placeholder).replace(r"\{value\}", r"([\w\.-]+)")
7
+ regex = re.compile(regex_pattern)
8
+
9
+ def replacer(match: re.Match) -> str:
10
+ key = match.group(1)
11
+ return values.get(key, match.group(0))
12
+
13
+ return regex.sub(replacer, text)
@@ -1,71 +1,52 @@
1
1
  from pydantic import BaseModel, Field
2
2
 
3
3
  from ai_review.config import settings
4
+ from ai_review.libs.template.render import render_template
4
5
 
5
6
 
6
7
  class PromptContextSchema(BaseModel):
7
- merge_request_title: str | None = None
8
- merge_request_description: str | None = None
8
+ merge_request_title: str = ""
9
+ merge_request_description: str = ""
9
10
 
10
- merge_request_author_name: str | None = None
11
- merge_request_author_username: str | None = None
11
+ merge_request_author_name: str = ""
12
+ merge_request_author_username: str = ""
12
13
 
13
- merge_request_reviewer: str | None = None
14
+ merge_request_reviewer: str = ""
14
15
  merge_request_reviewers: list[str] = Field(default_factory=list)
15
16
  merge_request_reviewers_usernames: list[str] = Field(default_factory=list)
16
17
 
17
18
  merge_request_assignees: list[str] = Field(default_factory=list)
18
19
  merge_request_assignees_usernames: list[str] = Field(default_factory=list)
19
20
 
20
- source_branch: str | None = None
21
- target_branch: str | None = None
21
+ source_branch: str = ""
22
+ target_branch: str = ""
22
23
 
23
24
  labels: list[str] = Field(default_factory=list)
24
25
  changed_files: list[str] = Field(default_factory=list)
25
26
 
26
27
  @property
27
- def reviewers_format(self) -> str:
28
- return ", ".join(self.merge_request_reviewers)
28
+ def render_values(self) -> dict[str, str]:
29
+ return {
30
+ "merge_request_title": self.merge_request_title,
31
+ "merge_request_description": self.merge_request_description,
29
32
 
30
- @property
31
- def reviewers_usernames_format(self) -> str:
32
- return ", ".join(self.merge_request_reviewers_usernames)
33
+ "merge_request_author_name": self.merge_request_author_name,
34
+ "merge_request_author_username": self.merge_request_author_username,
33
35
 
34
- @property
35
- def assignees_format(self) -> str:
36
- return ", ".join(self.merge_request_assignees)
36
+ "merge_request_reviewer": self.merge_request_reviewer,
37
+ "merge_request_reviewers": ", ".join(self.merge_request_reviewers),
38
+ "merge_request_reviewers_usernames": ", ".join(self.merge_request_reviewers_usernames),
37
39
 
38
- @property
39
- def assignees_usernames_format(self) -> str:
40
- return ", ".join(self.merge_request_assignees_usernames)
40
+ "merge_request_assignees": ", ".join(self.merge_request_assignees),
41
+ "merge_request_assignees_usernames": ", ".join(self.merge_request_assignees_usernames),
41
42
 
42
- @property
43
- def labels_format(self) -> str:
44
- return ", ".join(self.labels)
43
+ "source_branch": self.source_branch,
44
+ "target_branch": self.target_branch,
45
45
 
46
- @property
47
- def changed_files_format(self) -> str:
48
- return ", ".join(self.changed_files)
46
+ "labels": ", ".join(self.labels),
47
+ "changed_files": ", ".join(self.changed_files),
48
+ }
49
49
 
50
50
  def apply_format(self, prompt: str) -> str:
51
- return prompt.format(
52
- merge_request_title=self.merge_request_title or "",
53
- merge_request_description=self.merge_request_description or "",
54
-
55
- merge_request_author_name=self.merge_request_author_name or "",
56
- merge_request_author_username=self.merge_request_author_username or "",
57
-
58
- merge_request_reviewer=self.merge_request_reviewer or "",
59
- merge_request_reviewers=self.reviewers_format,
60
- merge_request_reviewers_usernames=self.reviewers_usernames_format,
61
-
62
- merge_request_assignees=self.assignees_format,
63
- merge_request_assignees_usernames=self.assignees_usernames_format,
64
-
65
- source_branch=self.source_branch or "",
66
- target_branch=self.target_branch or "",
67
-
68
- labels=self.labels_format,
69
- changed_files=self.changed_files_format,
70
- **settings.prompt.context
71
- )
51
+ values = {**self.render_values, **settings.prompt.context}
52
+ return render_template(prompt, values, settings.prompt.context_placeholder)
File without changes
@@ -0,0 +1,35 @@
1
+ import pytest
2
+ from httpx import AsyncClient
3
+ from pydantic import HttpUrl, SecretStr
4
+
5
+ from ai_review.clients.gitlab.client import get_gitlab_http_client, GitLabHTTPClient
6
+ from ai_review.clients.gitlab.mr.client import GitLabMergeRequestsHTTPClient
7
+ from ai_review.config import settings
8
+ from ai_review.libs.config.gitlab import GitLabPipelineConfig, GitLabHTTPClientConfig
9
+ from ai_review.libs.config.vcs import GitLabVCSConfig
10
+ from ai_review.libs.constants.vcs_provider import VCSProvider
11
+
12
+
13
+ @pytest.fixture(autouse=True)
14
+ def gitlab_http_client_config(monkeypatch: pytest.MonkeyPatch):
15
+ fake_config = GitLabVCSConfig(
16
+ provider=VCSProvider.GITLAB,
17
+ pipeline=GitLabPipelineConfig(
18
+ project_id="project-id",
19
+ merge_request_id="merge-request-id"
20
+ ),
21
+ http_client=GitLabHTTPClientConfig(
22
+ timeout=10,
23
+ api_url=HttpUrl("https://gitlab.com"),
24
+ api_token=SecretStr("fake-token"),
25
+ )
26
+ )
27
+ monkeypatch.setattr(settings, "vcs", fake_config)
28
+
29
+
30
+ def test_get_gitlab_http_client_builds_ok():
31
+ gitlab_http_client = get_gitlab_http_client()
32
+
33
+ assert isinstance(gitlab_http_client, GitLabHTTPClient)
34
+ assert isinstance(gitlab_http_client.mr, GitLabMergeRequestsHTTPClient)
35
+ assert isinstance(gitlab_http_client.mr.client, AsyncClient)
File without changes
@@ -0,0 +1,57 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from ai_review.libs.config.prompt import PromptConfig
6
+
7
+
8
+ def test_inline_prompt_files_or_default_uses_defaults(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
9
+ dummy_file = tmp_path / "dummy.md"
10
+ dummy_file.write_text("DUMMY")
11
+ monkeypatch.setattr("ai_review.libs.config.prompt.load_resource", lambda **_: dummy_file)
12
+
13
+ config = PromptConfig()
14
+ result = config.inline_prompt_files_or_default
15
+
16
+ assert result == [dummy_file]
17
+ assert config.load_inline() == ["DUMMY"]
18
+
19
+
20
+ def test_system_inline_prompts_none_returns_global(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
21
+ dummy_file = tmp_path / "global.md"
22
+ dummy_file.write_text("GLOBAL")
23
+ monkeypatch.setattr("ai_review.libs.config.prompt.load_resource", lambda **_: dummy_file)
24
+
25
+ config = PromptConfig(system_inline_prompt_files=None)
26
+ result = config.system_inline_prompt_files_or_default
27
+
28
+ assert result == [dummy_file]
29
+ assert config.load_system_inline() == ["GLOBAL"]
30
+
31
+
32
+ def test_system_inline_prompts_include_true(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
33
+ global_file = tmp_path / "global.md"
34
+ global_file.write_text("GLOBAL")
35
+ custom_file = tmp_path / "custom.md"
36
+ custom_file.write_text("CUSTOM")
37
+ monkeypatch.setattr("ai_review.libs.config.prompt.load_resource", lambda **_: global_file)
38
+
39
+ config = PromptConfig(system_inline_prompt_files=[custom_file], include_inline_system_prompts=True)
40
+ result = config.system_inline_prompt_files_or_default
41
+
42
+ assert global_file in result and custom_file in result
43
+ assert config.load_system_inline() == ["GLOBAL", "CUSTOM"]
44
+
45
+
46
+ def test_system_inline_prompts_include_false(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
47
+ global_file = tmp_path / "global.md"
48
+ global_file.write_text("GLOBAL")
49
+ custom_file = tmp_path / "custom.md"
50
+ custom_file.write_text("CUSTOM")
51
+ monkeypatch.setattr("ai_review.libs.config.prompt.load_resource", lambda **_: global_file)
52
+
53
+ config = PromptConfig(system_inline_prompt_files=[custom_file], include_inline_system_prompts=False)
54
+ result = config.system_inline_prompt_files_or_default
55
+
56
+ assert result == [custom_file]
57
+ assert config.load_system_inline() == ["CUSTOM"]
File without changes
@@ -0,0 +1,64 @@
1
+ from ai_review.libs.template.render import render_template
2
+
3
+
4
+ def test_replaces_single_variable():
5
+ text = "Hello, <<name>>!"
6
+ values = {"name": "Alice"}
7
+ result = render_template(text, values)
8
+ assert result == "Hello, Alice!"
9
+
10
+
11
+ def test_replaces_multiple_variables():
12
+ text = "User: <<user>>, Branch: <<branch>>"
13
+ values = {"user": "nikita", "branch": "main"}
14
+ result = render_template(text, values)
15
+ assert result == "User: nikita, Branch: main"
16
+
17
+
18
+ def test_missing_variable_keeps_placeholder():
19
+ text = "Hello, <<name>>, you are <<role>>"
20
+ values = {"name": "Bob"}
21
+ result = render_template(text, values)
22
+ assert result == "Hello, Bob, you are <<role>>"
23
+
24
+
25
+ def test_custom_placeholder_format():
26
+ text = "Hello, [[name]]!"
27
+ values = {"name": "Alice"}
28
+ result = render_template(text, values, placeholder="[[{value}]]")
29
+ assert result == "Hello, Alice!"
30
+
31
+
32
+ def test_placeholder_with_digits_and_underscores():
33
+ text = "Key: <<var_123>>"
34
+ values = {"var_123": "ok"}
35
+ result = render_template(text, values)
36
+ assert result == "Key: ok"
37
+
38
+
39
+ def test_no_placeholders_in_text():
40
+ text = "Nothing to replace"
41
+ values = {"name": "Alice"}
42
+ result = render_template(text, values)
43
+ assert result == "Nothing to replace"
44
+
45
+
46
+ def test_partial_overlap_does_not_replace():
47
+ text = "This is <<var>> and <<var_extra>>"
48
+ values = {"var": "A", "var_extra": "B"}
49
+ result = render_template(text, values)
50
+ assert result == "This is A and B"
51
+
52
+
53
+ def test_placeholder_with_dot_and_dash():
54
+ text = "Branch: <<feature-1.2>>"
55
+ values = {"feature-1.2": "ok"}
56
+ result = render_template(text, values)
57
+ assert result == "Branch: ok"
58
+
59
+
60
+ def test_multiple_same_placeholders():
61
+ text = "<<name>>, <<name>>, <<name>>!"
62
+ values = {"name": "Alice"}
63
+ result = render_template(text, values)
64
+ assert result == "Alice, Alice, Alice!"
@@ -1,3 +1,6 @@
1
+ import pytest
2
+
3
+ from ai_review.config import settings
1
4
  from ai_review.services.prompt.schema import PromptContextSchema
2
5
 
3
6
 
@@ -6,7 +9,7 @@ def test_apply_format_inserts_variables() -> None:
6
9
  merge_request_title="My MR",
7
10
  merge_request_author_username="nikita"
8
11
  )
9
- template = "Title: {merge_request_title}, Author: @{merge_request_author_username}"
12
+ template = "Title: <<merge_request_title>>, Author: @<<merge_request_author_username>>"
10
13
  result = context.apply_format(template)
11
14
  assert result == "Title: My MR, Author: @nikita"
12
15
 
@@ -19,10 +22,10 @@ def test_apply_format_with_lists() -> None:
19
22
  changed_files=["a.py", "b.py"],
20
23
  )
21
24
  template = (
22
- "Reviewers: {merge_request_reviewers}\n"
23
- "Usernames: {merge_request_reviewers_usernames}\n"
24
- "Labels: {labels}\n"
25
- "Files: {changed_files}"
25
+ "Reviewers: <<merge_request_reviewers>>\n"
26
+ "Usernames: <<merge_request_reviewers_usernames>>\n"
27
+ "Labels: <<labels>>\n"
28
+ "Files: <<changed_files>>"
26
29
  )
27
30
  result = context.apply_format(template)
28
31
  assert "Alice, Bob" in result
@@ -33,6 +36,36 @@ def test_apply_format_with_lists() -> None:
33
36
 
34
37
  def test_apply_format_handles_missing_fields() -> None:
35
38
  context = PromptContextSchema()
36
- template = "Title: {merge_request_title}, Reviewer: {merge_request_reviewer}"
39
+ template = "Title: <<merge_request_title>>, Reviewer: <<merge_request_reviewer>>"
37
40
  result = context.apply_format(template)
38
41
  assert result == "Title: , Reviewer: "
42
+
43
+
44
+ def test_apply_format_unknown_placeholder_kept() -> None:
45
+ context = PromptContextSchema()
46
+ template = "Unknown: <<does_not_exist>>"
47
+ result = context.apply_format(template)
48
+ assert result == "Unknown: <<does_not_exist>>"
49
+
50
+
51
+ def test_apply_format_multiple_occurrences() -> None:
52
+ context = PromptContextSchema(merge_request_title="My MR")
53
+ template = "<<merge_request_title>> again <<merge_request_title>>"
54
+ result = context.apply_format(template)
55
+ assert result == "My MR again My MR"
56
+
57
+
58
+ def test_apply_format_override_from_settings(monkeypatch: pytest.MonkeyPatch) -> None:
59
+ monkeypatch.setitem(settings.prompt.context, "merge_request_title", "Overridden")
60
+ context = PromptContextSchema(merge_request_title="Local Value")
61
+ template = "Title: <<merge_request_title>>"
62
+ result = context.apply_format(template)
63
+ assert result == "Title: Overridden"
64
+
65
+
66
+ def test_apply_format_prefers_override_even_if_empty(monkeypatch: pytest.MonkeyPatch) -> None:
67
+ monkeypatch.setitem(settings.prompt.context, "merge_request_title", "")
68
+ context = PromptContextSchema(merge_request_title="Local Value")
69
+ template = "Title: <<merge_request_title>>"
70
+ result = context.apply_format(template)
71
+ assert result == "Title: "
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xai-review
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: AI-powered code review tool
5
5
  Author-email: Nikita Filonov <nikita.filonov@example.com>
6
6
  Maintainer-email: Nikita Filonov <nikita.filonov@example.com>
@@ -211,19 +211,38 @@ ai-review:
211
211
  # Custom context variables
212
212
  # ===============================
213
213
  # You can inject custom variables into prompts via PROMPT__CONTEXT__*.
214
- # These will be available as placeholders {var} in all templates.
214
+ # These will be available in all templates through placeholders.
215
+ #
216
+ # Placeholder syntax is defined separately in PROMPT__CONTEXT_PLACEHOLDER.
217
+ # Default: <<{value}>>
215
218
  #
216
219
  # Example usage in prompt templates:
217
- # Project: {company_name}
218
- # Env: {environment}
219
- # Pipeline: {ci_pipeline_url}
220
+ # Project: <<company_name>>
221
+ # Env: <<environment>>
222
+ # Pipeline: <<ci_pipeline_url>>
220
223
  #
221
224
  # Values override built-in variables if names collide.
222
- # To avoid clashes, prefer namespaced keys (ci_pipeline_url, org_notify_handle, env_name).
225
+ # To avoid clashes, prefer namespaced keys
226
+ # (ci_pipeline_url, org_notify_handle, env_name).
223
227
  #
224
228
  # PROMPT__CONTEXT__ENVIRONMENT: "staging"
225
229
  # PROMPT__CONTEXT__COMPANY_NAME: "ACME Corp"
226
230
  # PROMPT__CONTEXT__CI_PIPELINE_URL: "https://gitlab.com/pipelines/123"
231
+ #
232
+ # ===============================
233
+ # Context placeholder
234
+ # ===============================
235
+ # Defines how placeholders are written in prompt templates.
236
+ # Must contain "{value}" which will be replaced by the variable name.
237
+ #
238
+ # Default: <<{value}>>
239
+ #
240
+ # Example:
241
+ # PROMPT__CONTEXT_PLACEHOLDER: "<<{value}>>"
242
+ # Template: "Env: <<environment>>"
243
+ # Result: "Env: staging"
244
+ #
245
+ # PROMPT__CONTEXT_PLACEHOLDER: "<<{value}>>"
227
246
 
228
247
  # ===============================
229
248
  # Review options
@@ -40,7 +40,7 @@ ai_review/libs/config/http.py,sha256=QsIj0yH1IYELOFBQ5AoqPZT0kGIIrQ19cxk1ozPRhLE
40
40
  ai_review/libs/config/llm.py,sha256=cK-e4NCQxnnixLATCsO8-r5k3zUWz1N0BdPCoqerORM,1824
41
41
  ai_review/libs/config/logger.py,sha256=oPmjpjf6EZwW7CgOjT8mOQdGnT98CLwXepiGB_ajZvU,384
42
42
  ai_review/libs/config/openai.py,sha256=vOYqhUq0ceEuNdQrQaHq44lVS5M648mB61Zc4YlfJVw,271
43
- ai_review/libs/config/prompt.py,sha256=xtgwsZ7NVmIEW3wpvfNiXJB3a1T_b_oUyfBOQ2Ye0g0,4389
43
+ ai_review/libs/config/prompt.py,sha256=e5jmHsfC6WpnkYZpTLT9TyKQfGtsbqJbxMkJBmLAWf0,4434
44
44
  ai_review/libs/config/review.py,sha256=LEZni68iH_0m4URPfN0d3F6yrrK7KSn-BwXf-7w2al8,1058
45
45
  ai_review/libs/config/vcs.py,sha256=FduvJJkGObh2LgTSapaMB6FABIvjX7E63yTwZF4p8CU,517
46
46
  ai_review/libs/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -58,6 +58,8 @@ ai_review/libs/http/event_hooks/base.py,sha256=cnSOOButTJYKeyb_OnGms1vXRfwfExP81
58
58
  ai_review/libs/http/event_hooks/logger.py,sha256=CcGk4dmxrOTwsM-QgTHQiJdI8cgLAG-Ay56iygOUCKU,546
59
59
  ai_review/libs/http/transports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
60
  ai_review/libs/http/transports/retry.py,sha256=WfY36SEMokjP0-uGv_OKlsaAVpMgePc1aYw3Eq4evTE,1145
61
+ ai_review/libs/template/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
+ ai_review/libs/template/render.py,sha256=PwLG46fXg8P3gZvmJB93P51G2IBdsEK2I8oDlLGmA-4,414
61
63
  ai_review/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
64
  ai_review/prompts/default_context.md,sha256=W8LvCMQxaX09NhPokwuxYLlA1jPCYor80GgZO8nhKbc,633
63
65
  ai_review/prompts/default_inline.md,sha256=UlxozaY5NtdZvA_d3qtAwIQSLP3C5JJh-mJtxGp_u0Q,257
@@ -94,7 +96,7 @@ ai_review/services/llm/openai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5N
94
96
  ai_review/services/llm/openai/client.py,sha256=WhMXNfH_G1NTlFkdRK5sgYvrCIE5ZQNfPhdYx49IXFk,1143
95
97
  ai_review/services/prompt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
98
  ai_review/services/prompt/adapter.py,sha256=xsJwBMmmuD0VIBbA7NCQuE6Zz6WyBoVJE3FuYMLHCxA,1005
97
- ai_review/services/prompt/schema.py,sha256=yQnovyydmMcb4TnEnLRhxuvccoa1mWo6PCbHeVDUD1g,2485
99
+ ai_review/services/prompt/schema.py,sha256=erAecUYzOWyZfixt-pjmPSnvcMDh5DajMd1b7_SPm_o,2052
98
100
  ai_review/services/prompt/service.py,sha256=AQG0YnlfXYtuADaZDhWD8NkPHx_zH4TCTOFPzMvZaSs,2078
99
101
  ai_review/services/review/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
102
  ai_review/services/review/service.py,sha256=8YhRFqhZAk2pAnkDaytKSCENlOeOti1brAJq3R9tVMY,8394
@@ -122,21 +124,27 @@ ai_review/tests/suites/clients/claude/test_schema.py,sha256=MUZXvEROgLNpUVHfCsH5
122
124
  ai_review/tests/suites/clients/gemini/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
123
125
  ai_review/tests/suites/clients/gemini/test_client.py,sha256=6hxpK7r7iZVbOzAffRNDJnA63-3Zxvqw5ynANPhBhBg,1066
124
126
  ai_review/tests/suites/clients/gemini/test_schema.py,sha256=88dU28m7sEWvGx6tqYl7if7weWYuVc8erlkFkKKI3bk,3109
127
+ ai_review/tests/suites/clients/gitlab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
128
+ ai_review/tests/suites/clients/gitlab/test_client.py,sha256=vXN7UZLC2yc7P7GZftpVvvUDycqR231ZFnfHZk97VLY,1325
125
129
  ai_review/tests/suites/clients/openai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
130
  ai_review/tests/suites/clients/openai/test_client.py,sha256=Ox5ifP1C_gSeDRacBa9DnXhe__Z8-WcbzR2JH_V3xKo,1050
127
131
  ai_review/tests/suites/clients/openai/test_schema.py,sha256=x1tamS4GC9pOTpjieKDbK2D73CVV4BkATppytwMevLo,1599
128
132
  ai_review/tests/suites/libs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
+ ai_review/tests/suites/libs/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
134
+ ai_review/tests/suites/libs/config/test_prompt.py,sha256=kDMTnykC54tTPfE6cqYRBbV8d5jzAKucXdJfwtqUybM,2242
129
135
  ai_review/tests/suites/libs/diff/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
130
136
  ai_review/tests/suites/libs/diff/test_models.py,sha256=RBFQ97LWhU8TlupxXkJ97ryAvJrSuOHLtT9biUBUMXg,3321
131
137
  ai_review/tests/suites/libs/diff/test_parser.py,sha256=rvWEVGIdaLBlDAnSevjRY7I1Zikj12d5GOgMk9QyHQQ,3013
132
138
  ai_review/tests/suites/libs/diff/test_tools.py,sha256=XkHJZ-b5veFz5oLKO09P7npaLN8lOzCnGR7e83Zv_mg,1953
139
+ ai_review/tests/suites/libs/template/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
140
+ ai_review/tests/suites/libs/template/test_render.py,sha256=n-ss5bd_hwc-RzYmqWmFM6KSlP1zLSnlsW1Yki12Bpw,1890
133
141
  ai_review/tests/suites/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
134
142
  ai_review/tests/suites/services/diff/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
143
  ai_review/tests/suites/services/diff/test_renderers.py,sha256=XFXaOTGAdPOJRb8w-1BOX-eci1C49mKvH4vckybrJSg,5641
136
144
  ai_review/tests/suites/services/diff/test_service.py,sha256=iFkGX9Vj2X44JU3eFsoHsg9o9353eKX-QCv_J9KxfzU,3162
137
145
  ai_review/tests/suites/services/diff/test_tools.py,sha256=HBQ3eCn-kLeb_k5iTgyr09x0VwLYSegSbxm0Qk9ZrCc,3543
138
146
  ai_review/tests/suites/services/prompt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
139
- ai_review/tests/suites/services/prompt/test_schema.py,sha256=tsilyAV7Fwzr2BULKkgt6ZAKTG0fLujvFnCztv9KjIk,1338
147
+ ai_review/tests/suites/services/prompt/test_schema.py,sha256=XkJk4N9ovgod7G3i6oZwRBjpd71sv0vtVDJhSOfIwGA,2660
140
148
  ai_review/tests/suites/services/prompt/test_service.py,sha256=XY7FsAhHUjr56AKVMDgCC7OpXOhj2UirB9pARmLjUuc,5236
141
149
  ai_review/tests/suites/services/review/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
150
  ai_review/tests/suites/services/review/inline/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -147,9 +155,9 @@ ai_review/tests/suites/services/review/policy/test_service.py,sha256=kRWT550OjWY
147
155
  ai_review/tests/suites/services/review/summary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
148
156
  ai_review/tests/suites/services/review/summary/test_schema.py,sha256=xSoydvABZldHaVDa0OBFvYrj8wMuZqUDN3MO-XdvxOI,819
149
157
  ai_review/tests/suites/services/review/summary/test_service.py,sha256=8UMvi_NL9frm280vD6Q1NCDrdI7K8YbXzoViIus-I2g,541
150
- xai_review-0.5.0.dist-info/licenses/LICENSE,sha256=p-v8m7Kmz4KKc7PcvsGiGEmCw9AiSXY4_ylOPy_u--Y,11343
151
- xai_review-0.5.0.dist-info/METADATA,sha256=zCeC477GXaai8-Y1ts0L0eDP-rBVyJG5RCwle8PX7nI,9037
152
- xai_review-0.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
153
- xai_review-0.5.0.dist-info/entry_points.txt,sha256=JyC5URanMi5io5P_PXQf7H_I1OGIpk5cZQhaPQ0g4Zs,53
154
- xai_review-0.5.0.dist-info/top_level.txt,sha256=sTsZbfzLoqvRZKdKa-BcxWvjlHdrpbeJ6DrGY0EuR0E,10
155
- xai_review-0.5.0.dist-info/RECORD,,
158
+ xai_review-0.6.0.dist-info/licenses/LICENSE,sha256=p-v8m7Kmz4KKc7PcvsGiGEmCw9AiSXY4_ylOPy_u--Y,11343
159
+ xai_review-0.6.0.dist-info/METADATA,sha256=OQT-Me4_2Dg1CHGNXQCoZBCE2sl1ceHgroCs0axcb3s,9640
160
+ xai_review-0.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
161
+ xai_review-0.6.0.dist-info/entry_points.txt,sha256=JyC5URanMi5io5P_PXQf7H_I1OGIpk5cZQhaPQ0g4Zs,53
162
+ xai_review-0.6.0.dist-info/top_level.txt,sha256=sTsZbfzLoqvRZKdKa-BcxWvjlHdrpbeJ6DrGY0EuR0E,10
163
+ xai_review-0.6.0.dist-info/RECORD,,