xai-review 0.5.0__tar.gz → 0.6.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.
Potentially problematic release.
This version of xai-review might be problematic. Click here for more details.
- {xai_review-0.5.0 → xai_review-0.6.0}/PKG-INFO +25 -6
- {xai_review-0.5.0 → xai_review-0.6.0}/README.md +24 -5
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/prompt.py +1 -0
- xai_review-0.6.0/ai_review/libs/template/render.py +13 -0
- xai_review-0.6.0/ai_review/services/prompt/schema.py +52 -0
- xai_review-0.6.0/ai_review/tests/suites/clients/gitlab/test_client.py +35 -0
- xai_review-0.6.0/ai_review/tests/suites/libs/config/test_prompt.py +57 -0
- xai_review-0.6.0/ai_review/tests/suites/libs/template/test_render.py +64 -0
- xai_review-0.6.0/ai_review/tests/suites/services/prompt/test_schema.py +71 -0
- xai_review-0.6.0/ai_review/tests/suites/services/review/__init__.py +0 -0
- xai_review-0.6.0/ai_review/tests/suites/services/review/inline/__init__.py +0 -0
- xai_review-0.6.0/ai_review/tests/suites/services/review/policy/__init__.py +0 -0
- xai_review-0.6.0/ai_review/tests/suites/services/review/summary/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/pyproject.toml +1 -1
- {xai_review-0.5.0 → xai_review-0.6.0}/xai_review.egg-info/PKG-INFO +25 -6
- {xai_review-0.5.0 → xai_review-0.6.0}/xai_review.egg-info/SOURCES.txt +8 -0
- xai_review-0.5.0/ai_review/services/prompt/schema.py +0 -71
- xai_review-0.5.0/ai_review/tests/suites/services/prompt/test_schema.py +0 -38
- {xai_review-0.5.0 → xai_review-0.6.0}/LICENSE +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/cli/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/cli/commands/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/cli/commands/run_context_review.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/cli/commands/run_inline_review.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/cli/commands/run_review.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/cli/commands/run_summary_review.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/cli/main.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/claude/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/claude/client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/claude/schema.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/gemini/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/gemini/client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/gemini/schema.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/gitlab/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/gitlab/client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/gitlab/mr/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/gitlab/mr/client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/gitlab/mr/schema/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/gitlab/mr/schema/changes.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/gitlab/mr/schema/comments.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/gitlab/mr/schema/discussions.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/openai/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/openai/client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/clients/openai/schema.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/config.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/asynchronous/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/asynchronous/gather.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/artifacts.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/base.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/claude.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/gemini.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/gitlab.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/http.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/llm.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/logger.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/openai.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/review.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/config/vcs.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/constants/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/constants/llm_provider.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/constants/vcs_provider.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/diff/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/diff/models.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/diff/parser.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/diff/tools.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/http/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/http/client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/http/event_hooks/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/http/event_hooks/base.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/http/event_hooks/logger.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/http/handlers.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/http/transports/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/http/transports/retry.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/logger.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/libs/resources.py +0 -0
- {xai_review-0.5.0/ai_review/prompts → xai_review-0.6.0/ai_review/libs/template}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/resources → xai_review-0.6.0/ai_review/prompts}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/prompts/default_context.md +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/prompts/default_inline.md +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/prompts/default_summary.md +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/prompts/default_system_context.md +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/prompts/default_system_inline.md +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/prompts/default_system_summary.md +0 -0
- {xai_review-0.5.0/ai_review/services → xai_review-0.6.0/ai_review/resources}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/resources/pricing.yaml +0 -0
- {xai_review-0.5.0/ai_review/services/artifacts → xai_review-0.6.0/ai_review/services}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/services/cost → xai_review-0.6.0/ai_review/services/artifacts}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/artifacts/schema.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/artifacts/service.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/artifacts/tools.py +0 -0
- {xai_review-0.5.0/ai_review/services/diff → xai_review-0.6.0/ai_review/services/cost}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/cost/schema.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/cost/service.py +0 -0
- {xai_review-0.5.0/ai_review/services/git → xai_review-0.6.0/ai_review/services/diff}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/diff/renderers.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/diff/schema.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/diff/service.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/diff/tools.py +0 -0
- {xai_review-0.5.0/ai_review/services/llm → xai_review-0.6.0/ai_review/services/git}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/git/service.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/git/types.py +0 -0
- {xai_review-0.5.0/ai_review/services/llm/claude → xai_review-0.6.0/ai_review/services/llm}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/services/llm/gemini → xai_review-0.6.0/ai_review/services/llm/claude}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/llm/claude/client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/llm/factory.py +0 -0
- {xai_review-0.5.0/ai_review/services/llm/openai → xai_review-0.6.0/ai_review/services/llm/gemini}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/llm/gemini/client.py +0 -0
- {xai_review-0.5.0/ai_review/services/prompt → xai_review-0.6.0/ai_review/services/llm/openai}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/llm/openai/client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/llm/types.py +0 -0
- {xai_review-0.5.0/ai_review/services/review → xai_review-0.6.0/ai_review/services/prompt}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/prompt/adapter.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/prompt/service.py +0 -0
- {xai_review-0.5.0/ai_review/services/review/inline → xai_review-0.6.0/ai_review/services/review}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/services/review/policy → xai_review-0.6.0/ai_review/services/review/inline}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/review/inline/schema.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/review/inline/service.py +0 -0
- {xai_review-0.5.0/ai_review/services/review/summary → xai_review-0.6.0/ai_review/services/review/policy}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/review/policy/service.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/review/service.py +0 -0
- {xai_review-0.5.0/ai_review/services/vcs → xai_review-0.6.0/ai_review/services/review/summary}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/review/summary/schema.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/review/summary/service.py +0 -0
- {xai_review-0.5.0/ai_review/services/vcs/gitlab → xai_review-0.6.0/ai_review/services/vcs}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/vcs/factory.py +0 -0
- {xai_review-0.5.0/ai_review/tests → xai_review-0.6.0/ai_review/services/vcs/gitlab}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/vcs/gitlab/client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/services/vcs/types.py +0 -0
- {xai_review-0.5.0/ai_review/tests/fixtures → xai_review-0.6.0/ai_review/tests}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites → xai_review-0.6.0/ai_review/tests/fixtures}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/fixtures/git.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/clients → xai_review-0.6.0/ai_review/tests/suites}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/clients/claude → xai_review-0.6.0/ai_review/tests/suites/clients}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/clients/gemini → xai_review-0.6.0/ai_review/tests/suites/clients/claude}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/clients/claude/test_client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/clients/claude/test_schema.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/clients/openai → xai_review-0.6.0/ai_review/tests/suites/clients/gemini}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/clients/gemini/test_client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/clients/gemini/test_schema.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/libs → xai_review-0.6.0/ai_review/tests/suites/clients/gitlab}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/libs/diff → xai_review-0.6.0/ai_review/tests/suites/clients/openai}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/clients/openai/test_client.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/clients/openai/test_schema.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/services → xai_review-0.6.0/ai_review/tests/suites/libs}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/services/diff → xai_review-0.6.0/ai_review/tests/suites/libs/config}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/services/prompt → xai_review-0.6.0/ai_review/tests/suites/libs/diff}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/libs/diff/test_models.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/libs/diff/test_parser.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/libs/diff/test_tools.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/services/review → xai_review-0.6.0/ai_review/tests/suites/libs/template}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/services/review/inline → xai_review-0.6.0/ai_review/tests/suites/services}/__init__.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/services/review/policy → xai_review-0.6.0/ai_review/tests/suites/services/diff}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/services/diff/test_renderers.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/services/diff/test_service.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/services/diff/test_tools.py +0 -0
- {xai_review-0.5.0/ai_review/tests/suites/services/review/summary → xai_review-0.6.0/ai_review/tests/suites/services/prompt}/__init__.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/services/prompt/test_service.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/services/review/inline/test_schema.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/services/review/inline/test_service.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/services/review/policy/test_service.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/services/review/summary/test_schema.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/ai_review/tests/suites/services/review/summary/test_service.py +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/setup.cfg +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/xai_review.egg-info/dependency_links.txt +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/xai_review.egg-info/entry_points.txt +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/xai_review.egg-info/requires.txt +0 -0
- {xai_review-0.5.0 → xai_review-0.6.0}/xai_review.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xai-review
|
|
3
|
-
Version: 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
|
|
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:
|
|
218
|
-
# Env:
|
|
219
|
-
# Pipeline:
|
|
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
|
|
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
|
|
@@ -187,19 +187,38 @@ ai-review:
|
|
|
187
187
|
# Custom context variables
|
|
188
188
|
# ===============================
|
|
189
189
|
# You can inject custom variables into prompts via PROMPT__CONTEXT__*.
|
|
190
|
-
# These will be available
|
|
190
|
+
# These will be available in all templates through placeholders.
|
|
191
|
+
#
|
|
192
|
+
# Placeholder syntax is defined separately in PROMPT__CONTEXT_PLACEHOLDER.
|
|
193
|
+
# Default: <<{value}>>
|
|
191
194
|
#
|
|
192
195
|
# Example usage in prompt templates:
|
|
193
|
-
# Project:
|
|
194
|
-
# Env:
|
|
195
|
-
# Pipeline:
|
|
196
|
+
# Project: <<company_name>>
|
|
197
|
+
# Env: <<environment>>
|
|
198
|
+
# Pipeline: <<ci_pipeline_url>>
|
|
196
199
|
#
|
|
197
200
|
# Values override built-in variables if names collide.
|
|
198
|
-
# To avoid clashes, prefer namespaced keys
|
|
201
|
+
# To avoid clashes, prefer namespaced keys
|
|
202
|
+
# (ci_pipeline_url, org_notify_handle, env_name).
|
|
199
203
|
#
|
|
200
204
|
# PROMPT__CONTEXT__ENVIRONMENT: "staging"
|
|
201
205
|
# PROMPT__CONTEXT__COMPANY_NAME: "ACME Corp"
|
|
202
206
|
# PROMPT__CONTEXT__CI_PIPELINE_URL: "https://gitlab.com/pipelines/123"
|
|
207
|
+
#
|
|
208
|
+
# ===============================
|
|
209
|
+
# Context placeholder
|
|
210
|
+
# ===============================
|
|
211
|
+
# Defines how placeholders are written in prompt templates.
|
|
212
|
+
# Must contain "{value}" which will be replaced by the variable name.
|
|
213
|
+
#
|
|
214
|
+
# Default: <<{value}>>
|
|
215
|
+
#
|
|
216
|
+
# Example:
|
|
217
|
+
# PROMPT__CONTEXT_PLACEHOLDER: "<<{value}>>"
|
|
218
|
+
# Template: "Env: <<environment>>"
|
|
219
|
+
# Result: "Env: staging"
|
|
220
|
+
#
|
|
221
|
+
# PROMPT__CONTEXT_PLACEHOLDER: "<<{value}>>"
|
|
203
222
|
|
|
204
223
|
# ===============================
|
|
205
224
|
# Review options
|
|
@@ -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
|
|
@@ -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)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
|
|
3
|
+
from ai_review.config import settings
|
|
4
|
+
from ai_review.libs.template.render import render_template
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PromptContextSchema(BaseModel):
|
|
8
|
+
merge_request_title: str = ""
|
|
9
|
+
merge_request_description: str = ""
|
|
10
|
+
|
|
11
|
+
merge_request_author_name: str = ""
|
|
12
|
+
merge_request_author_username: str = ""
|
|
13
|
+
|
|
14
|
+
merge_request_reviewer: str = ""
|
|
15
|
+
merge_request_reviewers: list[str] = Field(default_factory=list)
|
|
16
|
+
merge_request_reviewers_usernames: list[str] = Field(default_factory=list)
|
|
17
|
+
|
|
18
|
+
merge_request_assignees: list[str] = Field(default_factory=list)
|
|
19
|
+
merge_request_assignees_usernames: list[str] = Field(default_factory=list)
|
|
20
|
+
|
|
21
|
+
source_branch: str = ""
|
|
22
|
+
target_branch: str = ""
|
|
23
|
+
|
|
24
|
+
labels: list[str] = Field(default_factory=list)
|
|
25
|
+
changed_files: list[str] = Field(default_factory=list)
|
|
26
|
+
|
|
27
|
+
@property
|
|
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,
|
|
32
|
+
|
|
33
|
+
"merge_request_author_name": self.merge_request_author_name,
|
|
34
|
+
"merge_request_author_username": self.merge_request_author_username,
|
|
35
|
+
|
|
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),
|
|
39
|
+
|
|
40
|
+
"merge_request_assignees": ", ".join(self.merge_request_assignees),
|
|
41
|
+
"merge_request_assignees_usernames": ", ".join(self.merge_request_assignees_usernames),
|
|
42
|
+
|
|
43
|
+
"source_branch": self.source_branch,
|
|
44
|
+
"target_branch": self.target_branch,
|
|
45
|
+
|
|
46
|
+
"labels": ", ".join(self.labels),
|
|
47
|
+
"changed_files": ", ".join(self.changed_files),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
def apply_format(self, prompt: str) -> str:
|
|
51
|
+
values = {**self.render_values, **settings.prompt.context}
|
|
52
|
+
return render_template(prompt, values, settings.prompt.context_placeholder)
|
|
@@ -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)
|
|
@@ -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"]
|
|
@@ -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!"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from ai_review.config import settings
|
|
4
|
+
from ai_review.services.prompt.schema import PromptContextSchema
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_apply_format_inserts_variables() -> None:
|
|
8
|
+
context = PromptContextSchema(
|
|
9
|
+
merge_request_title="My MR",
|
|
10
|
+
merge_request_author_username="nikita"
|
|
11
|
+
)
|
|
12
|
+
template = "Title: <<merge_request_title>>, Author: @<<merge_request_author_username>>"
|
|
13
|
+
result = context.apply_format(template)
|
|
14
|
+
assert result == "Title: My MR, Author: @nikita"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_apply_format_with_lists() -> None:
|
|
18
|
+
context = PromptContextSchema(
|
|
19
|
+
merge_request_reviewers=["Alice", "Bob"],
|
|
20
|
+
merge_request_reviewers_usernames=["alice", "bob"],
|
|
21
|
+
labels=["bug", "feature"],
|
|
22
|
+
changed_files=["a.py", "b.py"],
|
|
23
|
+
)
|
|
24
|
+
template = (
|
|
25
|
+
"Reviewers: <<merge_request_reviewers>>\n"
|
|
26
|
+
"Usernames: <<merge_request_reviewers_usernames>>\n"
|
|
27
|
+
"Labels: <<labels>>\n"
|
|
28
|
+
"Files: <<changed_files>>"
|
|
29
|
+
)
|
|
30
|
+
result = context.apply_format(template)
|
|
31
|
+
assert "Alice, Bob" in result
|
|
32
|
+
assert "alice, bob" in result
|
|
33
|
+
assert "bug, feature" in result
|
|
34
|
+
assert "a.py, b.py" in result
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_apply_format_handles_missing_fields() -> None:
|
|
38
|
+
context = PromptContextSchema()
|
|
39
|
+
template = "Title: <<merge_request_title>>, Reviewer: <<merge_request_reviewer>>"
|
|
40
|
+
result = context.apply_format(template)
|
|
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: "
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xai-review
|
|
3
|
-
Version: 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
|
|
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:
|
|
218
|
-
# Env:
|
|
219
|
-
# Pipeline:
|
|
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
|
|
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
|
|
@@ -61,6 +61,8 @@ ai_review/libs/http/event_hooks/base.py
|
|
|
61
61
|
ai_review/libs/http/event_hooks/logger.py
|
|
62
62
|
ai_review/libs/http/transports/__init__.py
|
|
63
63
|
ai_review/libs/http/transports/retry.py
|
|
64
|
+
ai_review/libs/template/__init__.py
|
|
65
|
+
ai_review/libs/template/render.py
|
|
64
66
|
ai_review/prompts/__init__.py
|
|
65
67
|
ai_review/prompts/default_context.md
|
|
66
68
|
ai_review/prompts/default_inline.md
|
|
@@ -125,14 +127,20 @@ ai_review/tests/suites/clients/claude/test_schema.py
|
|
|
125
127
|
ai_review/tests/suites/clients/gemini/__init__.py
|
|
126
128
|
ai_review/tests/suites/clients/gemini/test_client.py
|
|
127
129
|
ai_review/tests/suites/clients/gemini/test_schema.py
|
|
130
|
+
ai_review/tests/suites/clients/gitlab/__init__.py
|
|
131
|
+
ai_review/tests/suites/clients/gitlab/test_client.py
|
|
128
132
|
ai_review/tests/suites/clients/openai/__init__.py
|
|
129
133
|
ai_review/tests/suites/clients/openai/test_client.py
|
|
130
134
|
ai_review/tests/suites/clients/openai/test_schema.py
|
|
131
135
|
ai_review/tests/suites/libs/__init__.py
|
|
136
|
+
ai_review/tests/suites/libs/config/__init__.py
|
|
137
|
+
ai_review/tests/suites/libs/config/test_prompt.py
|
|
132
138
|
ai_review/tests/suites/libs/diff/__init__.py
|
|
133
139
|
ai_review/tests/suites/libs/diff/test_models.py
|
|
134
140
|
ai_review/tests/suites/libs/diff/test_parser.py
|
|
135
141
|
ai_review/tests/suites/libs/diff/test_tools.py
|
|
142
|
+
ai_review/tests/suites/libs/template/__init__.py
|
|
143
|
+
ai_review/tests/suites/libs/template/test_render.py
|
|
136
144
|
ai_review/tests/suites/services/__init__.py
|
|
137
145
|
ai_review/tests/suites/services/diff/__init__.py
|
|
138
146
|
ai_review/tests/suites/services/diff/test_renderers.py
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field
|
|
2
|
-
|
|
3
|
-
from ai_review.config import settings
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class PromptContextSchema(BaseModel):
|
|
7
|
-
merge_request_title: str | None = None
|
|
8
|
-
merge_request_description: str | None = None
|
|
9
|
-
|
|
10
|
-
merge_request_author_name: str | None = None
|
|
11
|
-
merge_request_author_username: str | None = None
|
|
12
|
-
|
|
13
|
-
merge_request_reviewer: str | None = None
|
|
14
|
-
merge_request_reviewers: list[str] = Field(default_factory=list)
|
|
15
|
-
merge_request_reviewers_usernames: list[str] = Field(default_factory=list)
|
|
16
|
-
|
|
17
|
-
merge_request_assignees: list[str] = Field(default_factory=list)
|
|
18
|
-
merge_request_assignees_usernames: list[str] = Field(default_factory=list)
|
|
19
|
-
|
|
20
|
-
source_branch: str | None = None
|
|
21
|
-
target_branch: str | None = None
|
|
22
|
-
|
|
23
|
-
labels: list[str] = Field(default_factory=list)
|
|
24
|
-
changed_files: list[str] = Field(default_factory=list)
|
|
25
|
-
|
|
26
|
-
@property
|
|
27
|
-
def reviewers_format(self) -> str:
|
|
28
|
-
return ", ".join(self.merge_request_reviewers)
|
|
29
|
-
|
|
30
|
-
@property
|
|
31
|
-
def reviewers_usernames_format(self) -> str:
|
|
32
|
-
return ", ".join(self.merge_request_reviewers_usernames)
|
|
33
|
-
|
|
34
|
-
@property
|
|
35
|
-
def assignees_format(self) -> str:
|
|
36
|
-
return ", ".join(self.merge_request_assignees)
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def assignees_usernames_format(self) -> str:
|
|
40
|
-
return ", ".join(self.merge_request_assignees_usernames)
|
|
41
|
-
|
|
42
|
-
@property
|
|
43
|
-
def labels_format(self) -> str:
|
|
44
|
-
return ", ".join(self.labels)
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def changed_files_format(self) -> str:
|
|
48
|
-
return ", ".join(self.changed_files)
|
|
49
|
-
|
|
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
|
-
)
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from ai_review.services.prompt.schema import PromptContextSchema
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def test_apply_format_inserts_variables() -> None:
|
|
5
|
-
context = PromptContextSchema(
|
|
6
|
-
merge_request_title="My MR",
|
|
7
|
-
merge_request_author_username="nikita"
|
|
8
|
-
)
|
|
9
|
-
template = "Title: {merge_request_title}, Author: @{merge_request_author_username}"
|
|
10
|
-
result = context.apply_format(template)
|
|
11
|
-
assert result == "Title: My MR, Author: @nikita"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def test_apply_format_with_lists() -> None:
|
|
15
|
-
context = PromptContextSchema(
|
|
16
|
-
merge_request_reviewers=["Alice", "Bob"],
|
|
17
|
-
merge_request_reviewers_usernames=["alice", "bob"],
|
|
18
|
-
labels=["bug", "feature"],
|
|
19
|
-
changed_files=["a.py", "b.py"],
|
|
20
|
-
)
|
|
21
|
-
template = (
|
|
22
|
-
"Reviewers: {merge_request_reviewers}\n"
|
|
23
|
-
"Usernames: {merge_request_reviewers_usernames}\n"
|
|
24
|
-
"Labels: {labels}\n"
|
|
25
|
-
"Files: {changed_files}"
|
|
26
|
-
)
|
|
27
|
-
result = context.apply_format(template)
|
|
28
|
-
assert "Alice, Bob" in result
|
|
29
|
-
assert "alice, bob" in result
|
|
30
|
-
assert "bug, feature" in result
|
|
31
|
-
assert "a.py, b.py" in result
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def test_apply_format_handles_missing_fields() -> None:
|
|
35
|
-
context = PromptContextSchema()
|
|
36
|
-
template = "Title: {merge_request_title}, Reviewer: {merge_request_reviewer}"
|
|
37
|
-
result = context.apply_format(template)
|
|
38
|
-
assert result == "Title: , Reviewer: "
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|