From 5e82d0a3168e066fd054ab2f4e78aa6c240e2365 Mon Sep 17 00:00:00 2001 From: Makonike Date: Tue, 8 Jul 2025 11:51:30 +0800 Subject: [PATCH 01/42] feat: add streaming support for openai/qwq-plus model --- pr_agent/algo/__init__.py | 6 ++ .../algo/ai_handlers/litellm_ai_handler.py | 65 +++++++++++++++---- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index ed9edddc..0de83548 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -45,6 +45,7 @@ MAX_TOKENS = { 'command-nightly': 4096, 'deepseek/deepseek-chat': 128000, # 128K, but may be limited by config.max_model_tokens 'deepseek/deepseek-reasoner': 64000, # 64K, but may be limited by config.max_model_tokens + 'openai/qwq-plus': 131072, # 131K context length, but may be limited by config.max_model_tokens 'replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1': 4096, 'meta-llama/Llama-2-7b-chat-hf': 4096, 'vertex_ai/codechat-bison': 6144, @@ -193,3 +194,8 @@ CLAUDE_EXTENDED_THINKING_MODELS = [ "anthropic/claude-3-7-sonnet-20250219", "claude-3-7-sonnet-20250219" ] + +# Models that require streaming mode +STREAMING_REQUIRED_MODELS = [ + "openai/qwq-plus" +] diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index ec96d952..59a00045 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -5,7 +5,7 @@ import requests from litellm import acompletion from tenacity import retry, retry_if_exception_type, retry_if_not_exception_type, stop_after_attempt -from pr_agent.algo import CLAUDE_EXTENDED_THINKING_MODELS, NO_SUPPORT_TEMPERATURE_MODELS, SUPPORT_REASONING_EFFORT_MODELS, USER_MESSAGE_ONLY_MODELS +from pr_agent.algo import CLAUDE_EXTENDED_THINKING_MODELS, NO_SUPPORT_TEMPERATURE_MODELS, SUPPORT_REASONING_EFFORT_MODELS, USER_MESSAGE_ONLY_MODELS, STREAMING_REQUIRED_MODELS from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler from pr_agent.algo.utils import ReasoningEffort, get_version from pr_agent.config_loader import get_settings @@ -143,6 +143,9 @@ class LiteLLMAIHandler(BaseAiHandler): # Models that support extended thinking self.claude_extended_thinking_models = CLAUDE_EXTENDED_THINKING_MODELS + # Models that require streaming + self.streaming_required_models = STREAMING_REQUIRED_MODELS + def _get_azure_ad_token(self): """ Generates an access token using Azure AD credentials from settings. @@ -370,7 +373,21 @@ class LiteLLMAIHandler(BaseAiHandler): get_logger().info(f"\nSystem prompt:\n{system}") get_logger().info(f"\nUser prompt:\n{user}") - response = await acompletion(**kwargs) + # Check if model requires streaming + if model in self.streaming_required_models: + kwargs["stream"] = True + get_logger().info(f"Using streaming mode for model {model}") + response = await acompletion(**kwargs) + # Handle streaming response + resp, finish_reason = await self._handle_streaming_response(response) + else: + response = await acompletion(**kwargs) + # Handle non-streaming response + if response is None or len(response["choices"]) == 0: + raise openai.APIError + resp = response["choices"][0]['message']['content'] + finish_reason = response["choices"][0]["finish_reason"] + except openai.RateLimitError as e: get_logger().error(f"Rate limit error during LLM inference: {e}") raise @@ -380,19 +397,43 @@ class LiteLLMAIHandler(BaseAiHandler): except Exception as e: get_logger().warning(f"Unknown error during LLM inference: {e}") raise openai.APIError from e - if response is None or len(response["choices"]) == 0: - raise openai.APIError - else: - resp = response["choices"][0]['message']['content'] - finish_reason = response["choices"][0]["finish_reason"] - get_logger().debug(f"\nAI response:\n{resp}") - # log the full response for debugging + get_logger().debug(f"\nAI response:\n{resp}") + + # log the full response for debugging + if not (model in self.streaming_required_models): response_log = self.prepare_logs(response, system, user, resp, finish_reason) get_logger().debug("Full_response", artifact=response_log) - # for CLI debugging - if get_settings().config.verbosity_level >= 2: - get_logger().info(f"\nAI response:\n{resp}") + # for CLI debugging + if get_settings().config.verbosity_level >= 2: + get_logger().info(f"\nAI response:\n{resp}") return resp, finish_reason + + async def _handle_streaming_response(self, response): + """ + Handle streaming response from acompletion and collect the full response. + + Args: + response: The streaming response object from acompletion + + Returns: + tuple: (full_response_content, finish_reason) + """ + full_response = "" + finish_reason = None + + try: + async for chunk in response: + if chunk.choices and len(chunk.choices) > 0: + delta = chunk.choices[0].delta + if hasattr(delta, 'content') and delta.content: + full_response += delta.content + if chunk.choices[0].finish_reason: + finish_reason = chunk.choices[0].finish_reason + except Exception as e: + get_logger().error(f"Error handling streaming response: {e}") + raise + + return full_response, finish_reason From 2d8bee0d6d3984fe2b234ec65068e377112c28bc Mon Sep 17 00:00:00 2001 From: Makonike Date: Wed, 9 Jul 2025 15:04:18 +0800 Subject: [PATCH 02/42] feat: add validation for empty streaming responses in LiteLLM handler --- pr_agent/algo/ai_handlers/litellm_ai_handler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 59a00045..2b914c9f 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -436,4 +436,8 @@ class LiteLLMAIHandler(BaseAiHandler): get_logger().error(f"Error handling streaming response: {e}") raise + if not full_response: + get_logger().warning("Streaming response resulted in empty content") + raise openai.APIError("Empty streaming response received") + return full_response, finish_reason From 85e1e2d4ee1b806cff25e6309355194b593e530a Mon Sep 17 00:00:00 2001 From: Makonike Date: Wed, 9 Jul 2025 15:29:03 +0800 Subject: [PATCH 03/42] feat: add debug logging support for streaming models --- .../algo/ai_handlers/litellm_ai_handler.py | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 2b914c9f..787575c9 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -15,6 +15,23 @@ import json OPENAI_RETRIES = 5 +class MockResponse: + """Mock response object for streaming models to enable consistent logging.""" + + def __init__(self, resp, finish_reason): + self._data = { + "choices": [ + { + "message": {"content": resp}, + "finish_reason": finish_reason + } + ] + } + + def dict(self): + return self._data + + class LiteLLMAIHandler(BaseAiHandler): """ This class handles interactions with the OpenAI API for chat completions. @@ -401,9 +418,13 @@ class LiteLLMAIHandler(BaseAiHandler): get_logger().debug(f"\nAI response:\n{resp}") # log the full response for debugging - if not (model in self.streaming_required_models): + if model in self.streaming_required_models: + # for streaming, we don't have the full response object, so we create a mock one + mock_response = MockResponse(resp, finish_reason) + response_log = self.prepare_logs(mock_response, system, user, resp, finish_reason) + else: response_log = self.prepare_logs(response, system, user, resp, finish_reason) - get_logger().debug("Full_response", artifact=response_log) + get_logger().debug("Full_response", artifact=response_log) # for CLI debugging if get_settings().config.verbosity_level >= 2: From 31e25a596577615c998342950282186c3bfb149d Mon Sep 17 00:00:00 2001 From: Makonike Date: Wed, 9 Jul 2025 15:39:15 +0800 Subject: [PATCH 04/42] refactor(ai_handler): improve streaming response handling robustness --- pr_agent/algo/ai_handlers/litellm_ai_handler.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 787575c9..32d1420c 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -448,11 +448,13 @@ class LiteLLMAIHandler(BaseAiHandler): try: async for chunk in response: if chunk.choices and len(chunk.choices) > 0: - delta = chunk.choices[0].delta - if hasattr(delta, 'content') and delta.content: - full_response += delta.content - if chunk.choices[0].finish_reason: - finish_reason = chunk.choices[0].finish_reason + choice = chunk.choices[0] + delta = choice.delta + content = getattr(delta, 'content', None) + if content: + full_response += content + if choice.finish_reason: + finish_reason = choice.finish_reason except Exception as e: get_logger().error(f"Error handling streaming response: {e}") raise From 74df3f8bd58d2c52c287f36b5717e5067868aabc Mon Sep 17 00:00:00 2001 From: Makonike Date: Thu, 10 Jul 2025 15:14:25 +0800 Subject: [PATCH 05/42] fix(ai_handler): improve empty streaming response validation logic --- pr_agent/algo/ai_handlers/litellm_ai_handler.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 32d1420c..596ff7f9 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -459,8 +459,10 @@ class LiteLLMAIHandler(BaseAiHandler): get_logger().error(f"Error handling streaming response: {e}") raise - if not full_response: - get_logger().warning("Streaming response resulted in empty content") - raise openai.APIError("Empty streaming response received") + if not full_response and finish_reason is None: + get_logger().warning("Streaming response resulted in empty content with no finish reason") + raise openai.APIError("Empty streaming response received without proper completion") + elif not full_response and finish_reason: + get_logger().debug(f"Streaming response resulted in empty content but completed with finish_reason: {finish_reason}") return full_response, finish_reason From 5a00897cbe67f54452052a5efdaa90be9279ee94 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Sun, 13 Jul 2025 00:10:36 +0530 Subject: [PATCH 06/42] docs: add detailed configuration examples for GitHub Actions models --- docs/docs/installation/github.md | 424 ++++++++++++++++++ .../docs/usage-guide/automations_and_usage.md | 18 + 2 files changed, 442 insertions(+) diff --git a/docs/docs/installation/github.md b/docs/docs/installation/github.md index 7ca97a75..5431616c 100644 --- a/docs/docs/installation/github.md +++ b/docs/docs/installation/github.md @@ -51,6 +51,430 @@ When you open your next PR, you should see a comment from `github-actions` bot w See detailed usage instructions in the [USAGE GUIDE](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-action) +## Configuration Examples + +This section provides detailed, step-by-step examples for configuring PR-Agent with different models and advanced options in GitHub Actions. + +### Quick Start Examples + +#### Basic Setup (OpenAI Default) + +Copy this minimal workflow to get started with the default OpenAI models: + +```yaml +name: PR Agent +on: + pull_request: + types: [opened, reopened, ready_for_review] + issue_comment: +jobs: + pr_agent_job: + if: ${{ github.event.sender.type != 'Bot' }} + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + contents: write + steps: + - name: PR Agent action step + uses: qodo-ai/pr-agent@main + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + +#### Gemini Setup + +Ready-to-use workflow for Gemini models: + +```yaml +name: PR Agent (Gemini) +on: + pull_request: + types: [opened, reopened, ready_for_review] + issue_comment: +jobs: + pr_agent_job: + if: ${{ github.event.sender.type != 'Bot' }} + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + contents: write + steps: + - name: PR Agent action step + uses: qodo-ai/pr-agent@main + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + config.model: "gemini/gemini-1.5-flash" + config.fallback_models: '["gemini/gemini-1.5-flash"]' + GOOGLE_AI_STUDIO.GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" +``` + +#### Claude Setup + +Ready-to-use workflow for Claude models: + +```yaml +name: PR Agent (Claude) +on: + pull_request: + types: [opened, reopened, ready_for_review] + issue_comment: +jobs: + pr_agent_job: + if: ${{ github.event.sender.type != 'Bot' }} + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + contents: write + steps: + - name: PR Agent action step + uses: qodo-ai/pr-agent@main + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + config.model: "anthropic/claude-3-opus-20240229" + config.fallback_models: '["anthropic/claude-3-opus-20240229"]' + ANTHROPIC.KEY: ${{ secrets.ANTHROPIC_KEY }} + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" +``` + +### Basic Configuration with Tool Controls + +Start with this enhanced workflow that includes tool configuration: + +```yaml +on: + pull_request: + types: [opened, reopened, ready_for_review] + issue_comment: +jobs: + pr_agent_job: + if: ${{ github.event.sender.type != 'Bot' }} + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + contents: write + name: Run pr agent on every pull request, respond to user comments + steps: + - name: PR Agent action step + id: pragent + uses: qodo-ai/pr-agent@main + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Enable/disable automatic tools + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" + # Configure which PR events trigger the action + github_action_config.pr_actions: '["opened", "reopened", "ready_for_review", "review_requested"]' +``` + +### Switching Models + +#### Using Gemini (Google AI Studio) + +To use Gemini models instead of the default OpenAI models: + +```yaml + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Set the model to Gemini + config.model: "gemini/gemini-1.5-flash" + config.fallback_models: '["gemini/gemini-1.5-flash"]' + # Add your Gemini API key + GOOGLE_AI_STUDIO.GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + # Tool configuration + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" +``` + +**Required Secrets:** +- Add `GEMINI_API_KEY` to your repository secrets (get it from [Google AI Studio](https://aistudio.google.com/)) + +#### Using Claude (Anthropic) + +To use Claude models: + +```yaml + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Set the model to Claude + config.model: "anthropic/claude-3-opus-20240229" + config.fallback_models: '["anthropic/claude-3-opus-20240229"]' + # Add your Anthropic API key + ANTHROPIC.KEY: ${{ secrets.ANTHROPIC_KEY }} + # Tool configuration + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" +``` + +**Required Secrets:** +- Add `ANTHROPIC_KEY` to your repository secrets (get it from [Anthropic Console](https://console.anthropic.com/)) + +#### Using Azure OpenAI + +To use Azure OpenAI services: + +```yaml + env: + OPENAI_KEY: ${{ secrets.AZURE_OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Azure OpenAI configuration + OPENAI.API_TYPE: "azure" + OPENAI.API_VERSION: "2023-05-15" + OPENAI.API_BASE: ${{ secrets.AZURE_OPENAI_ENDPOINT }} + OPENAI.DEPLOYMENT_ID: ${{ secrets.AZURE_OPENAI_DEPLOYMENT }} + # Set the model to match your Azure deployment + config.model: "gpt-4o" + config.fallback_models: '["gpt-4o"]' + # Tool configuration + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" +``` + +**Required Secrets:** +- `AZURE_OPENAI_KEY`: Your Azure OpenAI API key +- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint URL +- `AZURE_OPENAI_DEPLOYMENT`: Your deployment name + +#### Using Local Models (Ollama) + +To use local models via Ollama: + +```yaml + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Set the model to a local Ollama model + config.model: "ollama/qwen2.5-coder:32b" + config.fallback_models: '["ollama/qwen2.5-coder:32b"]' + config.custom_model_max_tokens: "128000" + # Ollama configuration + OLLAMA.API_BASE: "http://localhost:11434" + # Tool configuration + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" +``` + +**Note:** For local models, you'll need to run Ollama on your GitHub Actions runner or use a self-hosted runner. + +### Advanced Configuration Options + +#### Custom Review Instructions + +Add specific instructions for the review process: + +```yaml + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Custom review instructions + pr_reviewer.extra_instructions: "Focus on security vulnerabilities and performance issues. Check for proper error handling." + # Tool configuration + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" +``` + +#### Language-Specific Configuration + +Configure for specific programming languages: + +```yaml + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Language-specific settings + pr_reviewer.extra_instructions: "Focus on Python best practices, type hints, and docstrings." + pr_code_suggestions.num_code_suggestions: "8" + pr_code_suggestions.suggestions_score_threshold: "7" + # Tool configuration + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" +``` + +#### Selective Tool Execution + +Run only specific tools automatically: + +```yaml + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Only run review and describe, skip improve + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "false" + # Only trigger on PR open and reopen + github_action_config.pr_actions: '["opened", "reopened"]' +``` + +### Using Configuration Files + +Instead of setting all options via environment variables, you can use a `.pr_agent.toml` file in your repository root: + +1. Create a `.pr_agent.toml` file in your repository root: + +```toml +[config] +model = "gemini/gemini-1.5-flash" +fallback_models = ["anthropic/claude-3-opus-20240229"] + +[pr_reviewer] +extra_instructions = "Focus on security issues and code quality." + +[pr_code_suggestions] +num_code_suggestions = 6 +suggestions_score_threshold = 7 +``` + +2. Use a simpler workflow file: + +```yaml +on: + pull_request: + types: [opened, reopened, ready_for_review] + issue_comment: +jobs: + pr_agent_job: + if: ${{ github.event.sender.type != 'Bot' }} + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + contents: write + name: Run pr agent on every pull request, respond to user comments + steps: + - name: PR Agent action step + id: pragent + uses: qodo-ai/pr-agent@main + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GOOGLE_AI_STUDIO.GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + ANTHROPIC.KEY: ${{ secrets.ANTHROPIC_KEY }} + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" +``` + +### Troubleshooting Common Issues + +#### Model Not Found Errors + +If you get model not found errors: + +1. **Check model name format**: Ensure you're using the correct model identifier format (e.g., `gemini/gemini-1.5-flash`, not just `gemini-1.5-flash`) + +2. **Verify API keys**: Make sure your API keys are correctly set as repository secrets + +3. **Check model availability**: Some models may not be available in all regions or may require specific access + +#### Environment Variable Format + +Remember these key points about environment variables: + +- Use dots (`.`) or double underscores (`__`) to separate sections and keys +- Boolean values should be strings: `"true"` or `"false"` +- Arrays should be JSON strings: `'["item1", "item2"]'` +- Model names are case-sensitive + +#### Rate Limiting + +If you encounter rate limiting: + +```yaml + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Add fallback models for better reliability + config.fallback_models: '["gpt-4o", "gpt-3.5-turbo"]' + # Increase timeout for slower models + config.ai_timeout: "300" + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" +``` + +#### Common Error Messages and Solutions + +**Error: "Model not found"** +- **Solution**: Check the model name format and ensure it matches the exact identifier from the [model list](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/algo/__init__.py) + +**Error: "API key not found"** +- **Solution**: Verify that your API key is correctly set as a repository secret and the environment variable name matches exactly + +**Error: "Rate limit exceeded"** +- **Solution**: Add fallback models or increase the `config.ai_timeout` value + +**Error: "Permission denied"** +- **Solution**: Ensure your workflow has the correct permissions set: + ```yaml + permissions: + issues: write + pull-requests: write + contents: write + ``` + +**Error: "Invalid JSON format"** +- **Solution**: Check that arrays are properly formatted as JSON strings: + ```yaml + # Correct + config.fallback_models: '["model1", "model2"]' + # Incorrect + config.fallback_models: "[model1, model2]" + ``` + +#### Debugging Tips + +1. **Enable verbose logging**: Add `config.verbosity_level: "2"` to see detailed logs +2. **Check GitHub Actions logs**: Look at the step output for specific error messages +3. **Test with minimal configuration**: Start with just the basic setup and add options one by one +4. **Verify secrets**: Double-check that all required secrets are set in your repository settings + +#### Performance Optimization + +For better performance with large repositories: + +```yaml + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Optimize for large PRs + config.large_patch_policy: "clip" + config.max_model_tokens: "32000" + config.patch_extra_lines_before: "3" + config.patch_extra_lines_after: "1" + github_action_config.auto_review: "true" + github_action_config.auto_describe: "true" + github_action_config.auto_improve: "true" +``` + +### Reference + +For more detailed configuration options, see: +- [Changing a model in PR-Agent](../usage-guide/changing_a_model.md) +- [Configuration options](../usage-guide/configuration_options.md) +- [Automations and usage](../usage-guide/automations_and_usage.md#github-action) + ### Using a specific release !!! tip "" diff --git a/docs/docs/usage-guide/automations_and_usage.md b/docs/docs/usage-guide/automations_and_usage.md index 0a634e77..1cf0e3d3 100644 --- a/docs/docs/usage-guide/automations_and_usage.md +++ b/docs/docs/usage-guide/automations_and_usage.md @@ -202,6 +202,24 @@ publish_labels = false to prevent Qodo Merge from publishing labels when running the `describe` tool. +#### Quick Reference: Model Configuration in GitHub Actions + +For detailed step-by-step examples of configuring different models (Gemini, Claude, Azure OpenAI, etc.) in GitHub Actions, see the [Configuration Examples](../installation/github.md#configuration-examples) section in the installation guide. + +**Common Model Configuration Patterns:** + +- **Gemini**: Set `config.model: "gemini/gemini-1.5-flash"` and `GOOGLE_AI_STUDIO.GEMINI_API_KEY` +- **Claude**: Set `config.model: "anthropic/claude-3-opus-20240229"` and `ANTHROPIC.KEY` +- **Azure OpenAI**: Set `OPENAI.API_TYPE: "azure"`, `OPENAI.API_BASE`, and `OPENAI.DEPLOYMENT_ID` +- **Local Models**: Set `config.model: "ollama/model-name"` and `OLLAMA.API_BASE` + +**Environment Variable Format:** +- Use dots (`.`) to separate sections and keys: `config.model`, `pr_reviewer.extra_instructions` +- Boolean values as strings: `"true"` or `"false"` +- Arrays as JSON strings: `'["item1", "item2"]'` + +For complete model configuration details, see [Changing a model in PR-Agent](changing_a_model.md). + ### GitLab Webhook After setting up a GitLab webhook, to control which commands will run automatically when a new MR is opened, you can set the `pr_commands` parameter in the configuration file, similar to the GitHub App: From 6108f96bffa23df8b409d1679b0a648a6904e160 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Sun, 13 Jul 2025 00:18:47 +0530 Subject: [PATCH 07/42] docs: update installation and usage guide to remove OPENAI_KEY for non-OpenAI models --- docs/docs/installation/github.md | 14 +++++++------- docs/docs/usage-guide/automations_and_usage.md | 5 +++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/docs/installation/github.md b/docs/docs/installation/github.md index 5431616c..5c00bf05 100644 --- a/docs/docs/installation/github.md +++ b/docs/docs/installation/github.md @@ -105,7 +105,6 @@ jobs: - name: PR Agent action step uses: qodo-ai/pr-agent@main env: - OPENAI_KEY: ${{ secrets.OPENAI_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} config.model: "gemini/gemini-1.5-flash" config.fallback_models: '["gemini/gemini-1.5-flash"]' @@ -137,7 +136,6 @@ jobs: - name: PR Agent action step uses: qodo-ai/pr-agent@main env: - OPENAI_KEY: ${{ secrets.OPENAI_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} config.model: "anthropic/claude-3-opus-20240229" config.fallback_models: '["anthropic/claude-3-opus-20240229"]' @@ -188,7 +186,6 @@ To use Gemini models instead of the default OpenAI models: ```yaml env: - OPENAI_KEY: ${{ secrets.OPENAI_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Set the model to Gemini config.model: "gemini/gemini-1.5-flash" @@ -204,13 +201,14 @@ To use Gemini models instead of the default OpenAI models: **Required Secrets:** - Add `GEMINI_API_KEY` to your repository secrets (get it from [Google AI Studio](https://aistudio.google.com/)) +**Note:** When using non-OpenAI models like Gemini, you don't need to set `OPENAI_KEY` - only the model-specific API key is required. + #### Using Claude (Anthropic) To use Claude models: ```yaml env: - OPENAI_KEY: ${{ secrets.OPENAI_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Set the model to Claude config.model: "anthropic/claude-3-opus-20240229" @@ -226,6 +224,8 @@ To use Claude models: **Required Secrets:** - Add `ANTHROPIC_KEY` to your repository secrets (get it from [Anthropic Console](https://console.anthropic.com/)) +**Note:** When using non-OpenAI models like Claude, you don't need to set `OPENAI_KEY` - only the model-specific API key is required. + #### Using Azure OpenAI To use Azure OpenAI services: @@ -367,7 +367,6 @@ jobs: id: pragent uses: qodo-ai/pr-agent@main env: - OPENAI_KEY: ${{ secrets.OPENAI_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GOOGLE_AI_STUDIO.GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} ANTHROPIC.KEY: ${{ secrets.ANTHROPIC_KEY }} @@ -417,10 +416,11 @@ If you encounter rate limiting: #### Common Error Messages and Solutions **Error: "Model not found"** -- **Solution**: Check the model name format and ensure it matches the exact identifier from the [model list](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/algo/__init__.py) +- **Solution**: Check the model name format and ensure it matches the exact identifier. See the [Changing a model in PR-Agent](../usage-guide/changing_a_model.md) guide for supported models and their correct identifiers. **Error: "API key not found"** - **Solution**: Verify that your API key is correctly set as a repository secret and the environment variable name matches exactly +- **Note**: For non-OpenAI models (Gemini, Claude, etc.), you only need the model-specific API key, not `OPENAI_KEY` **Error: "Rate limit exceeded"** - **Solution**: Add fallback models or increase the `config.ai_timeout` value @@ -720,4 +720,4 @@ After you set up AWS CodeCommit using the instructions above, here is an example PYTHONPATH="/PATH/TO/PROJECTS/pr-agent" python pr_agent/cli.py \ --pr_url https://us-east-1.console.aws.amazon.com/codesuite/codecommit/repositories/MY_REPO_NAME/pull-requests/321 \ review -``` +``` \ No newline at end of file diff --git a/docs/docs/usage-guide/automations_and_usage.md b/docs/docs/usage-guide/automations_and_usage.md index 1cf0e3d3..1cec00d9 100644 --- a/docs/docs/usage-guide/automations_and_usage.md +++ b/docs/docs/usage-guide/automations_and_usage.md @@ -208,8 +208,9 @@ For detailed step-by-step examples of configuring different models (Gemini, Clau **Common Model Configuration Patterns:** -- **Gemini**: Set `config.model: "gemini/gemini-1.5-flash"` and `GOOGLE_AI_STUDIO.GEMINI_API_KEY` -- **Claude**: Set `config.model: "anthropic/claude-3-opus-20240229"` and `ANTHROPIC.KEY` +- **OpenAI**: Set `config.model: "gpt-4o"` and `OPENAI_KEY` +- **Gemini**: Set `config.model: "gemini/gemini-1.5-flash"` and `GOOGLE_AI_STUDIO.GEMINI_API_KEY` (no `OPENAI_KEY` needed) +- **Claude**: Set `config.model: "anthropic/claude-3-opus-20240229"` and `ANTHROPIC.KEY` (no `OPENAI_KEY` needed) - **Azure OpenAI**: Set `OPENAI.API_TYPE: "azure"`, `OPENAI.API_BASE`, and `OPENAI.DEPLOYMENT_ID` - **Local Models**: Set `config.model: "ollama/model-name"` and `OLLAMA.API_BASE` From 3aaa727e058ebd520681e406a248b5172d3cd7ca Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Sun, 13 Jul 2025 00:20:03 +0530 Subject: [PATCH 08/42] docs: update fallback model configuration and clarify self-hosted runner requirements --- docs/docs/installation/github.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/docs/installation/github.md b/docs/docs/installation/github.md index 5c00bf05..928409b4 100644 --- a/docs/docs/installation/github.md +++ b/docs/docs/installation/github.md @@ -138,7 +138,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} config.model: "anthropic/claude-3-opus-20240229" - config.fallback_models: '["anthropic/claude-3-opus-20240229"]' + config.fallback_models: '["anthropic/claude-3-haiku-20240307"]' ANTHROPIC.KEY: ${{ secrets.ANTHROPIC_KEY }} github_action_config.auto_review: "true" github_action_config.auto_describe: "true" @@ -212,7 +212,7 @@ To use Claude models: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Set the model to Claude config.model: "anthropic/claude-3-opus-20240229" - config.fallback_models: '["anthropic/claude-3-opus-20240229"]' + config.fallback_models: '["anthropic/claude-3-haiku-20240307"]' # Add your Anthropic API key ANTHROPIC.KEY: ${{ secrets.ANTHROPIC_KEY }} # Tool configuration @@ -273,7 +273,7 @@ To use local models via Ollama: github_action_config.auto_improve: "true" ``` -**Note:** For local models, you'll need to run Ollama on your GitHub Actions runner or use a self-hosted runner. +**Note:** For local models, you'll need to use a self-hosted runner with Ollama installed, as GitHub Actions hosted runners cannot access localhost services. ### Advanced Configuration Options @@ -438,9 +438,9 @@ If you encounter rate limiting: - **Solution**: Check that arrays are properly formatted as JSON strings: ```yaml # Correct - config.fallback_models: '["model1", "model2"]' - # Incorrect - config.fallback_models: "[model1, model2]" +config.fallback_models: '["model1", "model2"]' +# Incorrect (interpreted as a YAML list, not a string) +config.fallback_models: ["model1", "model2"] ``` #### Debugging Tips From 11fb6ccc7e03b6060d312f7aa73add2af0fba3a8 Mon Sep 17 00:00:00 2001 From: Makonike Date: Sun, 13 Jul 2025 22:37:14 +0800 Subject: [PATCH 09/42] refactor(ai_handler): compact streaming path to reduce main flow impact --- .../algo/ai_handlers/litellm_ai_handler.py | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 596ff7f9..380a7893 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -390,20 +390,8 @@ class LiteLLMAIHandler(BaseAiHandler): get_logger().info(f"\nSystem prompt:\n{system}") get_logger().info(f"\nUser prompt:\n{user}") - # Check if model requires streaming - if model in self.streaming_required_models: - kwargs["stream"] = True - get_logger().info(f"Using streaming mode for model {model}") - response = await acompletion(**kwargs) - # Handle streaming response - resp, finish_reason = await self._handle_streaming_response(response) - else: - response = await acompletion(**kwargs) - # Handle non-streaming response - if response is None or len(response["choices"]) == 0: - raise openai.APIError - resp = response["choices"][0]['message']['content'] - finish_reason = response["choices"][0]["finish_reason"] + # Get completion with automatic streaming detection + resp, finish_reason, response_obj = await self._get_completion(model, **kwargs) except openai.RateLimitError as e: get_logger().error(f"Rate limit error during LLM inference: {e}") @@ -418,12 +406,7 @@ class LiteLLMAIHandler(BaseAiHandler): get_logger().debug(f"\nAI response:\n{resp}") # log the full response for debugging - if model in self.streaming_required_models: - # for streaming, we don't have the full response object, so we create a mock one - mock_response = MockResponse(resp, finish_reason) - response_log = self.prepare_logs(mock_response, system, user, resp, finish_reason) - else: - response_log = self.prepare_logs(response, system, user, resp, finish_reason) + response_log = self.prepare_logs(response_obj, system, user, resp, finish_reason) get_logger().debug("Full_response", artifact=response_log) # for CLI debugging @@ -466,3 +449,23 @@ class LiteLLMAIHandler(BaseAiHandler): get_logger().debug(f"Streaming response resulted in empty content but completed with finish_reason: {finish_reason}") return full_response, finish_reason + + async def _get_completion(self, model, **kwargs): + """ + Wrapper that automatically handles streaming for required models. + """ + if model in self.streaming_required_models: + kwargs["stream"] = True + get_logger().info(f"Using streaming mode for model {model}") + response = await acompletion(**kwargs) + resp, finish_reason = await self._handle_streaming_response(response) + # Create MockResponse for streaming since we don't have the full response object + mock_response = MockResponse(resp, finish_reason) + return resp, finish_reason, mock_response + else: + response = await acompletion(**kwargs) + if response is None or len(response["choices"]) == 0: + raise openai.APIError + return (response["choices"][0]['message']['content'], + response["choices"][0]["finish_reason"], + response) From 8c7680d85ddef4bf77620acd0c77a2d373ce243e Mon Sep 17 00:00:00 2001 From: Makonike Date: Sun, 13 Jul 2025 22:57:43 +0800 Subject: [PATCH 10/42] refactor(ai_handler): add a return statement or raise an exception in the elif branch --- pr_agent/algo/ai_handlers/litellm_ai_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 380a7893..ad9747be 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -447,7 +447,7 @@ class LiteLLMAIHandler(BaseAiHandler): raise openai.APIError("Empty streaming response received without proper completion") elif not full_response and finish_reason: get_logger().debug(f"Streaming response resulted in empty content but completed with finish_reason: {finish_reason}") - + raise openai.APIError(f"Streaming response completed with finish_reason '{finish_reason}' but no content received") return full_response, finish_reason async def _get_completion(self, model, **kwargs): From c4dd07b3b889756d32b5b48b119d1a247722eb75 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Sun, 13 Jul 2025 23:11:22 +0530 Subject: [PATCH 11/42] squashing revert commits --- docs/overrides/partials/integrations/analytics/custom.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overrides/partials/integrations/analytics/custom.html b/docs/overrides/partials/integrations/analytics/custom.html index 76b6f73a..63dc6503 100644 --- a/docs/overrides/partials/integrations/analytics/custom.html +++ b/docs/overrides/partials/integrations/analytics/custom.html @@ -3,5 +3,5 @@ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); - })(window,document,'script','dataLayer','GTM-M6PJSFV'); + })(window,document,'script','dataLayer','GTM-5C9KZBM3'); From 0e9cf274eff00b33fa238fb91d4617c51bf08bde Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 13 Jul 2025 21:23:04 +0300 Subject: [PATCH 12/42] refactor(ai_handler): move streaming response handling and Azure token generation to helpers --- .../algo/ai_handlers/litellm_ai_handler.py | 115 ++---------------- pr_agent/algo/ai_handlers/litellm_helpers.py | 113 +++++++++++++++++ 2 files changed, 120 insertions(+), 108 deletions(-) create mode 100644 pr_agent/algo/ai_handlers/litellm_helpers.py diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 8e2e1617..a21e3a71 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -7,29 +7,14 @@ from tenacity import retry, retry_if_exception_type, retry_if_not_exception_type from pr_agent.algo import CLAUDE_EXTENDED_THINKING_MODELS, NO_SUPPORT_TEMPERATURE_MODELS, SUPPORT_REASONING_EFFORT_MODELS, USER_MESSAGE_ONLY_MODELS, STREAMING_REQUIRED_MODELS from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler +from pr_agent.algo.ai_handlers.litellm_helpers import _handle_streaming_response, MockResponse, _get_azure_ad_token, \ + _process_litellm_extra_body from pr_agent.algo.utils import ReasoningEffort, get_version from pr_agent.config_loader import get_settings from pr_agent.log import get_logger import json -OPENAI_RETRIES = 5 - - -class MockResponse: - """Mock response object for streaming models to enable consistent logging.""" - - def __init__(self, resp, finish_reason): - self._data = { - "choices": [ - { - "message": {"content": resp}, - "finish_reason": finish_reason - } - ] - } - - def dict(self): - return self._data +MODEL_RETRIES = 2 class LiteLLMAIHandler(BaseAiHandler): @@ -127,7 +112,7 @@ class LiteLLMAIHandler(BaseAiHandler): if get_settings().get("AZURE_AD.CLIENT_ID", None): self.azure = True # Generate access token using Azure AD credentials from settings - access_token = self._get_azure_ad_token() + access_token = _get_azure_ad_token() litellm.api_key = access_token openai.api_key = access_token @@ -163,26 +148,6 @@ class LiteLLMAIHandler(BaseAiHandler): # Models that require streaming self.streaming_required_models = STREAMING_REQUIRED_MODELS - def _get_azure_ad_token(self): - """ - Generates an access token using Azure AD credentials from settings. - Returns: - str: The access token - """ - from azure.identity import ClientSecretCredential - try: - credential = ClientSecretCredential( - tenant_id=get_settings().azure_ad.tenant_id, - client_id=get_settings().azure_ad.client_id, - client_secret=get_settings().azure_ad.client_secret - ) - # Get token for Azure OpenAI service - token = credential.get_token("https://cognitiveservices.azure.com/.default") - return token.token - except Exception as e: - get_logger().error(f"Failed to get Azure AD token: {e}") - raise - def prepare_logs(self, response, system, user, resp, finish_reason): response_log = response.dict().copy() response_log['system'] = system @@ -195,37 +160,6 @@ class LiteLLMAIHandler(BaseAiHandler): response_log['main_pr_language'] = 'unknown' return response_log - def _process_litellm_extra_body(self, kwargs: dict) -> dict: - """ - Process LITELLM.EXTRA_BODY configuration and update kwargs accordingly. - - Args: - kwargs: The current kwargs dictionary to update - - Returns: - Updated kwargs dictionary - - Raises: - ValueError: If extra_body contains invalid JSON, unsupported keys, or colliding keys - """ - allowed_extra_body_keys = {"processing_mode", "service_tier"} - extra_body = getattr(getattr(get_settings(), "litellm", None), "extra_body", None) - if extra_body: - try: - litellm_extra_body = json.loads(extra_body) - if not isinstance(litellm_extra_body, dict): - raise ValueError("LITELLM.EXTRA_BODY must be a JSON object") - unsupported_keys = set(litellm_extra_body.keys()) - allowed_extra_body_keys - if unsupported_keys: - raise ValueError(f"LITELLM.EXTRA_BODY contains unsupported keys: {', '.join(unsupported_keys)}. Allowed keys: {', '.join(allowed_extra_body_keys)}") - colliding_keys = kwargs.keys() & litellm_extra_body.keys() - if colliding_keys: - raise ValueError(f"LITELLM.EXTRA_BODY cannot override existing parameters: {', '.join(colliding_keys)}") - kwargs.update(litellm_extra_body) - except json.JSONDecodeError as e: - raise ValueError(f"LITELLM.EXTRA_BODY contains invalid JSON: {str(e)}") - return kwargs - def _configure_claude_extended_thinking(self, model: str, kwargs: dict) -> dict: """ Configure Claude extended thinking parameters if applicable. @@ -326,7 +260,7 @@ class LiteLLMAIHandler(BaseAiHandler): @retry( retry=retry_if_exception_type(openai.APIError) & retry_if_not_exception_type(openai.RateLimitError), - stop=stop_after_attempt(OPENAI_RETRIES), + stop=stop_after_attempt(MODEL_RETRIES), ) async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2, img_path: str = None): try: @@ -416,7 +350,7 @@ class LiteLLMAIHandler(BaseAiHandler): kwargs["extra_headers"] = litellm_extra_headers # Support for custom OpenAI body fields (e.g., Flex Processing) - kwargs = self._process_litellm_extra_body(kwargs) + kwargs = _process_litellm_extra_body(kwargs) get_logger().debug("Prompts", artifact={"system": system, "user": user}) @@ -449,41 +383,6 @@ class LiteLLMAIHandler(BaseAiHandler): return resp, finish_reason - async def _handle_streaming_response(self, response): - """ - Handle streaming response from acompletion and collect the full response. - - Args: - response: The streaming response object from acompletion - - Returns: - tuple: (full_response_content, finish_reason) - """ - full_response = "" - finish_reason = None - - try: - async for chunk in response: - if chunk.choices and len(chunk.choices) > 0: - choice = chunk.choices[0] - delta = choice.delta - content = getattr(delta, 'content', None) - if content: - full_response += content - if choice.finish_reason: - finish_reason = choice.finish_reason - except Exception as e: - get_logger().error(f"Error handling streaming response: {e}") - raise - - if not full_response and finish_reason is None: - get_logger().warning("Streaming response resulted in empty content with no finish reason") - raise openai.APIError("Empty streaming response received without proper completion") - elif not full_response and finish_reason: - get_logger().debug(f"Streaming response resulted in empty content but completed with finish_reason: {finish_reason}") - raise openai.APIError(f"Streaming response completed with finish_reason '{finish_reason}' but no content received") - return full_response, finish_reason - async def _get_completion(self, model, **kwargs): """ Wrapper that automatically handles streaming for required models. @@ -492,7 +391,7 @@ class LiteLLMAIHandler(BaseAiHandler): kwargs["stream"] = True get_logger().info(f"Using streaming mode for model {model}") response = await acompletion(**kwargs) - resp, finish_reason = await self._handle_streaming_response(response) + resp, finish_reason = await _handle_streaming_response(response) # Create MockResponse for streaming since we don't have the full response object mock_response = MockResponse(resp, finish_reason) return resp, finish_reason, mock_response diff --git a/pr_agent/algo/ai_handlers/litellm_helpers.py b/pr_agent/algo/ai_handlers/litellm_helpers.py new file mode 100644 index 00000000..5f30655d --- /dev/null +++ b/pr_agent/algo/ai_handlers/litellm_helpers.py @@ -0,0 +1,113 @@ +import json + +import openai +from azure.identity import ClientSecretCredential + +from pr_agent.config_loader import get_settings +from pr_agent.log import get_logger + + +async def _handle_streaming_response(response): + """ + Handle streaming response from acompletion and collect the full response. + + Args: + response: The streaming response object from acompletion + + Returns: + tuple: (full_response_content, finish_reason) + """ + full_response = "" + finish_reason = None + + try: + async for chunk in response: + if chunk.choices and len(chunk.choices) > 0: + choice = chunk.choices[0] + delta = choice.delta + content = getattr(delta, 'content', None) + if content: + full_response += content + if choice.finish_reason: + finish_reason = choice.finish_reason + except Exception as e: + get_logger().error(f"Error handling streaming response: {e}") + raise + + if not full_response and finish_reason is None: + get_logger().warning("Streaming response resulted in empty content with no finish reason") + raise openai.APIError("Empty streaming response received without proper completion") + elif not full_response and finish_reason: + get_logger().debug(f"Streaming response resulted in empty content but completed with finish_reason: {finish_reason}") + raise openai.APIError(f"Streaming response completed with finish_reason '{finish_reason}' but no content received") + return full_response, finish_reason + + +class MockResponse: + """Mock response object for streaming models to enable consistent logging.""" + + def __init__(self, resp, finish_reason): + self._data = { + "choices": [ + { + "message": {"content": resp}, + "finish_reason": finish_reason + } + ] + } + + def dict(self): + return self._data + + +def _get_azure_ad_token(): + """ + Generates an access token using Azure AD credentials from settings. + Returns: + str: The access token + """ + from azure.identity import ClientSecretCredential + try: + credential = ClientSecretCredential( + tenant_id=get_settings().azure_ad.tenant_id, + client_id=get_settings().azure_ad.client_id, + client_secret=get_settings().azure_ad.client_secret + ) + # Get token for Azure OpenAI service + token = credential.get_token("https://cognitiveservices.azure.com/.default") + return token.token + except Exception as e: + get_logger().error(f"Failed to get Azure AD token: {e}") + raise + + +def _process_litellm_extra_body(kwargs: dict) -> dict: + """ + Process LITELLM.EXTRA_BODY configuration and update kwargs accordingly. + + Args: + kwargs: The current kwargs dictionary to update + + Returns: + Updated kwargs dictionary + + Raises: + ValueError: If extra_body contains invalid JSON, unsupported keys, or colliding keys + """ + allowed_extra_body_keys = {"processing_mode", "service_tier"} + extra_body = getattr(getattr(get_settings(), "litellm", None), "extra_body", None) + if extra_body: + try: + litellm_extra_body = json.loads(extra_body) + if not isinstance(litellm_extra_body, dict): + raise ValueError("LITELLM.EXTRA_BODY must be a JSON object") + unsupported_keys = set(litellm_extra_body.keys()) - allowed_extra_body_keys + if unsupported_keys: + raise ValueError(f"LITELLM.EXTRA_BODY contains unsupported keys: {', '.join(unsupported_keys)}. Allowed keys: {', '.join(allowed_extra_body_keys)}") + colliding_keys = kwargs.keys() & litellm_extra_body.keys() + if colliding_keys: + raise ValueError(f"LITELLM.EXTRA_BODY cannot override existing parameters: {', '.join(colliding_keys)}") + kwargs.update(litellm_extra_body) + except json.JSONDecodeError as e: + raise ValueError(f"LITELLM.EXTRA_BODY contains invalid JSON: {str(e)}") + return kwargs \ No newline at end of file From 8e0c5c87846284748cdaa4c3a7e63282271b3dd6 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 13 Jul 2025 21:29:53 +0300 Subject: [PATCH 13/42] refactor(ai_handler): remove model parameter from _get_completion and handle it within the method --- pr_agent/algo/ai_handlers/litellm_ai_handler.py | 5 +++-- pr_agent/algo/ai_handlers/litellm_helpers.py | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index a21e3a71..63e9aaa1 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -359,7 +359,7 @@ class LiteLLMAIHandler(BaseAiHandler): get_logger().info(f"\nUser prompt:\n{user}") # Get completion with automatic streaming detection - resp, finish_reason, response_obj = await self._get_completion(model, **kwargs) + resp, finish_reason, response_obj = await self._get_completion(**kwargs) except openai.RateLimitError as e: get_logger().error(f"Rate limit error during LLM inference: {e}") @@ -383,10 +383,11 @@ class LiteLLMAIHandler(BaseAiHandler): return resp, finish_reason - async def _get_completion(self, model, **kwargs): + async def _get_completion(self, **kwargs): """ Wrapper that automatically handles streaming for required models. """ + model = kwargs["model"] if model in self.streaming_required_models: kwargs["stream"] = True get_logger().info(f"Using streaming mode for model {model}") diff --git a/pr_agent/algo/ai_handlers/litellm_helpers.py b/pr_agent/algo/ai_handlers/litellm_helpers.py index 5f30655d..7324301e 100644 --- a/pr_agent/algo/ai_handlers/litellm_helpers.py +++ b/pr_agent/algo/ai_handlers/litellm_helpers.py @@ -1,7 +1,6 @@ import json import openai -from azure.identity import ClientSecretCredential from pr_agent.config_loader import get_settings from pr_agent.log import get_logger From 380437b44f980cf903dd20867e92553e0a55cad8 Mon Sep 17 00:00:00 2001 From: furikake6000 Date: Mon, 14 Jul 2025 15:26:30 +0000 Subject: [PATCH 14/42] feat: add support for filtering ignored files in Bitbucket Server provider --- pr_agent/algo/file_filter.py | 2 ++ pr_agent/git_providers/bitbucket_server_provider.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pr_agent/algo/file_filter.py b/pr_agent/algo/file_filter.py index 109aede9..0812759f 100644 --- a/pr_agent/algo/file_filter.py +++ b/pr_agent/algo/file_filter.py @@ -56,6 +56,8 @@ def filter_ignored(files, platform = 'github'): files_o.append(f) continue files = files_o + elif platform == 'bitbucket_server': + files = [f for f in files if not r.match(f['path']['toString'])] elif platform == 'gitlab': # files = [f for f in files if (f['new_path'] and not r.match(f['new_path']))] files_o = [] diff --git a/pr_agent/git_providers/bitbucket_server_provider.py b/pr_agent/git_providers/bitbucket_server_provider.py index 8417d910..43cdad7c 100644 --- a/pr_agent/git_providers/bitbucket_server_provider.py +++ b/pr_agent/git_providers/bitbucket_server_provider.py @@ -10,6 +10,7 @@ from requests.exceptions import HTTPError import shlex import subprocess +from ..algo.file_filter import filter_ignored from ..algo.git_patch_processing import decode_if_bytes from ..algo.language_handler import is_valid_file from ..algo.types import EDIT_TYPE, FilePatchInfo @@ -244,7 +245,8 @@ class BitbucketServerProvider(GitProvider): original_file_content_str = "" new_file_content_str = "" - changes = self.bitbucket_client.get_pull_requests_changes(self.workspace_slug, self.repo_slug, self.pr_num) + changes_original = list(self.bitbucket_client.get_pull_requests_changes(self.workspace_slug, self.repo_slug, self.pr_num)) + changes = filter_ignored(changes_original, 'bitbucket_server') for change in changes: file_path = change['path']['toString'] if not is_valid_file(file_path.split("/")[-1]): From c0d7fd8c36262fb7d0314fbf732ca72f24b81f66 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 16 Jul 2025 08:31:16 +0300 Subject: [PATCH 15/42] update docs --- docs/docs/usage-guide/automations_and_usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/usage-guide/automations_and_usage.md b/docs/docs/usage-guide/automations_and_usage.md index 0a634e77..0d3fe87f 100644 --- a/docs/docs/usage-guide/automations_and_usage.md +++ b/docs/docs/usage-guide/automations_and_usage.md @@ -30,7 +30,7 @@ verbosity_level=2 This is useful for debugging or experimenting with different tools. 3. **git provider**: The [git_provider](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L5) field in a configuration file determines the GIT provider that will be used by Qodo Merge. Currently, the following providers are supported: -`github` **(default)**, `gitlab`, `bitbucket`, `azure`, `codecommit`, `local`,`gitea`, and `gerrit`. +`github` **(default)**, `gitlab`, `bitbucket`, `azure`, `codecommit`, `local`, and `gitea`. ### CLI Health Check From e87fdd0ab5ad4dc723eff5353c6c3824bbea4864 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Thu, 17 Jul 2025 15:43:45 +0300 Subject: [PATCH 16/42] docs: add compliance tool documentation and update feature tables --- README.md | 27 +-- docs/docs/index.md | 1 + docs/docs/tools/compliance.md | 300 ++++++++++++++++++++++++++++++++++ docs/docs/tools/index.md | 1 + docs/mkdocs.yml | 1 + 5 files changed, 317 insertions(+), 13 deletions(-) create mode 100644 docs/docs/tools/compliance.md diff --git a/README.md b/README.md index a4cacda2..42b54828 100644 --- a/README.md +++ b/README.md @@ -119,19 +119,20 @@ Here are some advantages of PR-Agent: PR-Agent and Qodo Merge offer comprehensive pull request functionalities integrated with various git providers: -| | | GitHub | GitLab | Bitbucket | Azure DevOps | Gitea | -|---------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|:-----:| -| [TOOLS](https://qodo-merge-docs.qodo.ai/tools/) | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | | -| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | | -| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | | -| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | | -| | [Add Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | | -| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | | -| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | -| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | +| | | GitHub | GitLab | Bitbucket | Azure DevOps | Gitea | +|---------------------------------------------------------|----------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|:-----:| +| [TOOLS](https://qodo-merge-docs.qodo.ai/tools/) | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | | +| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | | +| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | | +| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | | +| | [Add Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | | +| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | | +| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | +| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | +| | [Compliance](https://qodo-merge-docs.qodo.ai/tools/compliance/) 💎 | ✅ | ✅ | ✅ | | | | | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | | | | [Generate Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | | | [Generate Tests](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | | diff --git a/docs/docs/index.md b/docs/docs/index.md index f67a8dbb..b4738d80 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -39,6 +39,7 @@ PR-Agent and Qodo Merge offer comprehensive pull request functionalities integra | | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | | | | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | | | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | +| | [Compliance](https://qodo-merge-docs.qodo.ai/tools/compliance/) 💎 | ✅ | ✅ | ✅ | | | | | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | | | | [Generate Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | | | [Generate Tests](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | | diff --git a/docs/docs/tools/compliance.md b/docs/docs/tools/compliance.md new file mode 100644 index 00000000..4e5ae111 --- /dev/null +++ b/docs/docs/tools/compliance.md @@ -0,0 +1,300 @@ +`Platforms supported: GitHub, GitLab, Bitbucket` + +## Overview + +The `compliance` tool performs comprehensive compliance checks on PR code changes, validating them against security standards, ticket requirements, and custom organizational compliance checklists. + +=== "Fully Compliant" + ![compliance_overview](https://codium.ai/images/pr_agent/compliance_full.png){width=256} + +=== "Partially Compliant" + ![compliance_overview](https://codium.ai/images/pr_agent/compliance_partial.png){width=256} + +___ + +[//]: # (???+ note "The following features are available only for Qodo Merge 💎 users:") + +[//]: # ( - Custom compliance checklists and hierarchical compliance checklists) + +[//]: # ( - Ticket compliance validation with Jira/Linear integration) + +[//]: # ( - Auto-approval based on compliance status) + +[//]: # ( - Compliance labels and automated enforcement) + +## Example Usage + +### Manual Triggering + +Invoke the tool manually by commenting `/compliance` on any PR. The compliance results are presented in a comprehensive table: + +To edit [configurations](#configuration-options) related to the `compliance` tool, use the following template: + +```toml +/compliance --pr_compliance.some_config1=... --pr_compliance.some_config2=... +``` + +For example, you can enable ticket compliance labels by running: + +```toml +/compliance --pr_compliance.enable_ticket_labels=true +``` + +### Automatic Triggering + + +The tool can be triggered automatically every time a new PR is [opened](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app-automatic-tools-when-a-new-pr-is-opened), or in a [push](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/?h=push#github-app-automatic-tools-for-push-actions-commits-to-an-open-pr) event to an existing PR. + +To run the `compliance` tool automatically when a PR is opened, define the following in the [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/): + +```toml +[github_app] # for example +pr_commands = [ + "/compliance", + ... +] + +``` + +## Compliance Categories + +The compliance tool evaluates three main categories: + + +### 1. Security Compliance + +Scans for security vulnerabilities and potential exploits in the PR code changes: + +- **Verified Security Concerns** 🔴: Clear security vulnerabilities that require immediate attention +- **Possible Security Risks** ⚪: Potential security issues that need human verification +- **No Security Concerns** 🟢: No security vulnerabilities were detected + +Examples of security issues: + +- Exposure of sensitive information (API keys, passwords, secrets) +- SQL injection vulnerabilities +- Cross-site scripting (XSS) risks +- Cross-site request forgery (CSRF) vulnerabilities +- Insecure data handling patterns + + +### 2. Ticket Compliance + +???+ tip "How to set up ticket compliance" + Follow the guide on how to set up [ticket compliance](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) with Qodo Merge. + +Validates that PR changes fulfill the requirements specified in linked tickets: + +- **Fully Compliant** 🟢: All ticket requirements are satisfied +- **Partially Compliant** 🟡: Some requirements are met, others need attention +- **Not Compliant** 🔴: Clear violations of ticket requirements +- **Requires Verification** ⚪: Requirements that need human review + + +### 3. Custom Compliance + +Validates against an organization-specific compliance checklist: + +- **Fully Compliant** 🟢: All custom compliance are satisfied +- **Not Compliant** 🔴: Violations of custom compliance +- **Requires Verification** ⚪: Compliance that need human assessment + +## Custom Compliance + +### Setting Up Custom Compliance + +Each compliance is defined in a YAML file, as follows: +- `title`: Used to provide a clear name for the compliance +- `compliance_label`: Used to automatically generate labels for non-compliance issues +- `objective`, `success_criteria`, and `failure_criteria`: These fields are used to clearly define what constitutes compliance + +???+ tip "Example of a compliance checklist" + + ```yaml + # pr_compliance_checklist.yaml + pr_compliances: + - title: "Error Handling" + compliance_label: true + objective: "All external API calls must have proper error handling" + success_criteria: "Try-catch blocks around external calls with appropriate logging" + failure_criteria: "External API calls without error handling or logging" + + ... + ``` + +???+ tip "Writing effective compliance checklist" + - Avoid overly complex or subjective compliances that are hard to verify + - Keep compliances focused on security, business requirements, and critical standards + - Use clear, actionable language that developers can understand + - Focus on meaningful compliance requirements, not style preferences + + +### Global Hierarchical Compliance + +Qodo Merge supports hierarchical compliance checklists using a dedicated global configuration repository. + +#### Setting up global hierarchical compliance + +1\. Create a new repository named `pr-agent-settings` in your organization/workspace. + +2\. Build the folder hierarchy in your `pr-agent-settings` repository: + +```bash +pr-agent-settings/ +├── metadata.yaml # Maps repos/folders to compliance paths +└── compliance_standards/ # Root for all compliance definitions + ├── global/ # Global compliance, inherited widely + │ └── pr_compliance_checklist.yaml + ├── groups/ # For groups of repositories + │ ├── frontend_repos/ + │ │ └── pr_compliance_checklist.yaml + │ └── backend_repos/ + │ └── pr_compliance_checklist.yaml + ├── qodo-merge/ # For standalone repositories + │ └── pr_compliance_checklist.yaml + └── qodo-monorepo/ # For monorepo-specific compliance + ├── pr_compliance_checklist.yaml # Root level monorepo compliance + ├── qodo-github/ # Subproject compliance + │ └── pr_compliance_checklist.yaml + └── qodo-gitlab/ # Another subproject + └── pr_compliance_checklist.yaml +``` + +3\. Define the metadata file `metadata.yaml` in the `pr-agent-settings` root: + +```yaml +# Standalone repos +qodo-merge: + pr_compliance_checklist_paths: + - "qodo-merge" + +# Group-associated repos +repo_b: + pr_compliance_checklist_paths: + - "groups/backend_repos" + +# Multi-group repos +repo_c: + pr_compliance_checklist_paths: + - "groups/frontend_repos" + - "groups/backend_repos" + +# Monorepo with subprojects +qodo-monorepo: + pr_compliance_checklist_paths: + - "qodo-monorepo" + monorepo_subprojects: + frontend: + pr_compliance_checklist_paths: + - "qodo-monorepo/qodo-github" + backend: + pr_compliance_checklist_paths: + - "qodo-monorepo/qodo-gitlab" +``` + +4\. Set the following configuration: + +```toml +[pr_compliance] +enable_global_pr_compliance = true +``` + +???- info "Compliance priority and fallback behavior" + + 1\. **Primary**: Global hierarchical compliance checklists from the `pr-agent-settings` repository: + + 1.1 If the repository is mapped in `metadata.yaml`, it uses the specified paths + + 1.2 For monorepos, it automatically collects compliance checklists matching PR file paths + + 1.3 If no mapping exists, it falls back to the global compliance checklists + + 2. **Fallback**: Local repository wiki `pr_compliance_checklist.yaml` file: + + 2.1 Used when global compliance checklists are not found or configured + + 2.2 Acts as a safety net for repositories not yet configured in the global system + + 2.3 Local wiki compliance checklists are completely ignored when compliance checklists are successfully loaded + + +## Configuration Options + +???+ example "General options" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
extra_instructionsOptional extra instructions for the tool. For example: "focus on the changes in the file X. Ignore changes in ...". Default is empty string.
persistent_commentIf set to true, the compliance comment will be persistent, meaning that every new compliance request will edit the previous one. Default is true.
enable_user_defined_compliance_labelsIf set to true, the tool will add labels for custom compliance violations. Default is true.
enable_estimate_effort_to_reviewIf set to true, the tool will estimate the effort required to review the PR (1-5 scale). Default is true.
enable_todo_scanIf set to true, the tool will scan for TODO comments in the PR code. Default is false.
enable_update_pr_compliance_checkboxIf set to true, the tool will add an update checkbox to refresh compliance status. Default is true.
enable_help_textIf set to true, the tool will display a help text in the comment. Default is false.
+ +???+ example "Security compliance options" + + + + + + + + + + +
enable_security_complianceIf set to true, the tool will check for security vulnerabilities. Default is true.
enable_compliance_labels_securityIf set to true, the tool will add security-related labels to the PR. Default is true.
+ +???+ example "Ticket compliance options" + + + + + + + + + + + + + + +
enable_ticket_labelsIf set to true, the tool will add ticket compliance labels to the PR. Default is false.
enable_no_ticket_labelsIf set to true, the tool will add a label when no ticket is found. Default is false.
check_pr_additional_contentIf set to true, the tool will check if the PR contains content not related to the ticket. Default is false.
+ + +## Usage Tips + +### Blocking PRs Based on Compliance + +!!! tip "" + You can configure CI/CD Actions to prevent merging PRs with specific compliance labels: + + - `Possible security concern` - Block PRs with potential security issues + - `Failed compliance check` - Block PRs that violate custom compliance checklists + + Implement a dedicated [GitHub Action](https://medium.com/sequra-tech/quick-tip-block-pull-request-merge-using-labels-6cc326936221) to enforce these checklists. + + +The compliance tool provides a comprehensive view of PR quality and adherence to organizational standards, helping teams maintain consistent code quality and security practices while ensuring that development work aligns with business requirements. diff --git a/docs/docs/tools/index.md b/docs/docs/tools/index.md index e50e0785..cce65834 100644 --- a/docs/docs/tools/index.md +++ b/docs/docs/tools/index.md @@ -14,6 +14,7 @@ Here is a list of Qodo Merge tools, each with a dedicated page that explains how | **💎 [Add Documentation (`/add_docs`](./documentation.md))** | Generates documentation to methods/functions/classes that changed in the PR | | **💎 [Analyze (`/analyze`](./analyze.md))** | Identify code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component | | **💎 [CI Feedback (`/checks ci_job`](./ci_feedback.md))** | Automatically generates feedback and analysis for a failed CI job | +| **💎 [Compliance (`/compliance`](./compliance.md))** | Comprehensive compliance checks for security, ticket requirements, and custom organizational rules | | **💎 [Custom Prompt (`/custom_prompt`](./custom_prompt.md))** | Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user | | **💎 [Generate Custom Labels (`/generate_labels`](./custom_labels.md))** | Generates custom labels for the PR, based on specific guidelines defined by the user | | **💎 [Generate Tests (`/test`](./test.md))** | Automatically generates unit tests for a selected component, based on the PR code changes | diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 894de407..746341a6 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -34,6 +34,7 @@ nav: - 💎 Add Documentation: 'tools/documentation.md' - 💎 Analyze: 'tools/analyze.md' - 💎 CI Feedback: 'tools/ci_feedback.md' + - 💎 Compliance: 'tools/compliance.md' - 💎 Custom Prompt: 'tools/custom_prompt.md' - 💎 Generate Labels: 'tools/custom_labels.md' - 💎 Generate Tests: 'tools/test.md' From d7d4b7de8922fb61d682e010ab827b8e00be8ad3 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Thu, 17 Jul 2025 15:47:31 +0300 Subject: [PATCH 17/42] docs: improve compliance tool overview and remove redundant conclusion --- docs/docs/tools/compliance.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/docs/tools/compliance.md b/docs/docs/tools/compliance.md index 4e5ae111..27c2e70a 100644 --- a/docs/docs/tools/compliance.md +++ b/docs/docs/tools/compliance.md @@ -2,7 +2,7 @@ ## Overview -The `compliance` tool performs comprehensive compliance checks on PR code changes, validating them against security standards, ticket requirements, and custom organizational compliance checklists. +The `compliance` tool performs comprehensive compliance checks on PR code changes, validating them against security standards, ticket requirements, and custom organizational compliance checklists, thereby helping teams maintain consistent code quality and security practices while ensuring that development work aligns with business requirements. === "Fully Compliant" ![compliance_overview](https://codium.ai/images/pr_agent/compliance_full.png){width=256} @@ -296,5 +296,3 @@ enable_global_pr_compliance = true Implement a dedicated [GitHub Action](https://medium.com/sequra-tech/quick-tip-block-pull-request-merge-using-labels-6cc326936221) to enforce these checklists. - -The compliance tool provides a comprehensive view of PR quality and adherence to organizational standards, helping teams maintain consistent code quality and security practices while ensuring that development work aligns with business requirements. From 7efeeb1de845c6ccf05ea237f74a81911de19eea Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Thu, 17 Jul 2025 15:50:30 +0300 Subject: [PATCH 18/42] docs: fix indentation in compliance tool directory structure example --- docs/docs/tools/compliance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/compliance.md b/docs/docs/tools/compliance.md index 27c2e70a..edbf4e4f 100644 --- a/docs/docs/tools/compliance.md +++ b/docs/docs/tools/compliance.md @@ -149,7 +149,7 @@ pr-agent-settings/ │ ├── frontend_repos/ │ │ └── pr_compliance_checklist.yaml │ └── backend_repos/ - │ └── pr_compliance_checklist.yaml + │ └── pr_compliance_checklist.yaml ├── qodo-merge/ # For standalone repositories │ └── pr_compliance_checklist.yaml └── qodo-monorepo/ # For monorepo-specific compliance From bec70dc96a3868810ecc11e55e87aec91fcba8ef Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Thu, 17 Jul 2025 18:10:59 +0300 Subject: [PATCH 19/42] docs: fix alignment in compliance tool monorepo directory structure example --- docs/docs/tools/compliance.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tools/compliance.md b/docs/docs/tools/compliance.md index edbf4e4f..f55c6be5 100644 --- a/docs/docs/tools/compliance.md +++ b/docs/docs/tools/compliance.md @@ -154,9 +154,9 @@ pr-agent-settings/ │ └── pr_compliance_checklist.yaml └── qodo-monorepo/ # For monorepo-specific compliance ├── pr_compliance_checklist.yaml # Root level monorepo compliance - ├── qodo-github/ # Subproject compliance + ├── qodo-github/ # Subproject compliance │ └── pr_compliance_checklist.yaml - └── qodo-gitlab/ # Another subproject + └── qodo-gitlab/ # Another subproject └── pr_compliance_checklist.yaml ``` From 754d47f1875a4839abbc6a98858886ca33b937e4 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 18 Jul 2025 08:51:48 +0300 Subject: [PATCH 20/42] refactor(pr_description): redesign changes walkthrough section and improve file processing --- pr_agent/algo/utils.py | 5 +- .../git_providers/azuredevops_provider.py | 2 +- pr_agent/settings/configuration.toml | 2 +- pr_agent/tools/pr_description.py | 60 +++++++++++++------ 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index 00d52a14..eaa8ce47 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -70,7 +70,8 @@ class ReasoningEffort(str, Enum): class PRDescriptionHeader(str, Enum): - CHANGES_WALKTHROUGH = "### **Changes walkthrough** 📝" + DIAGRAM_WALKTHROUGH = "Diagram Walkthrough" + FILE_WALKTHROUGH = "File Walkthrough" def get_setting(key: str) -> Any: @@ -1284,7 +1285,7 @@ def process_description(description_full: str) -> Tuple[str, List]: if not description_full: return "", [] - description_split = description_full.split(PRDescriptionHeader.CHANGES_WALKTHROUGH.value) + description_split = description_full.split(PRDescriptionHeader.FILE_WALKTHROUGH.value) base_description_str = description_split[0] changes_walkthrough_str = "" files = [] diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index 283f28c7..cba81654 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -380,7 +380,7 @@ class AzureDevopsProvider(GitProvider): pr_body = pr_body[:ind] if len(pr_body) > MAX_PR_DESCRIPTION_AZURE_LENGTH: - changes_walkthrough_text = PRDescriptionHeader.CHANGES_WALKTHROUGH.value + changes_walkthrough_text = PRDescriptionHeader.FILE_WALKTHROUGH.value ind = pr_body.find(changes_walkthrough_text) if ind != -1: pr_body = pr_body[:ind] diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index e75cfa56..3613d136 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -106,7 +106,7 @@ extra_instructions = "" enable_pr_type=true final_update_message = true enable_help_text=false -enable_help_comment=true +enable_help_comment=false enable_pr_diagram=true # adds a section with a diagram of the PR changes # describe as comment publish_description_as_comment=false diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index f0446b05..c9ecdbe2 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -128,7 +128,7 @@ class PRDescription: pr_title, pr_body, changes_walkthrough, pr_file_changes = self._prepare_pr_answer() if not self.git_provider.is_supported( "publish_file_comments") or not get_settings().pr_description.inline_file_summary: - pr_body += "\n\n" + changes_walkthrough + pr_body += "\n\n" + changes_walkthrough + "___\n\n" get_logger().debug("PR output", artifact={"title": pr_title, "body": pr_body}) # Add help text if gfm_markdown is supported @@ -331,7 +331,8 @@ class PRDescription: else: original_prediction_dict = original_prediction_loaded if original_prediction_dict: - filenames_predicted = [file.get('filename', '').strip() for file in original_prediction_dict.get('pr_files', [])] + files = original_prediction_dict.get('pr_files', []) + filenames_predicted = [file.get('filename', '').strip() for file in files if isinstance(file, dict)] else: filenames_predicted = [] @@ -555,15 +556,11 @@ class PRDescription: """ # Iterate over the dictionary items and append the key and value to 'markdown_text' in a markdown format - markdown_text = "" # Don't display 'PR Labels' if 'labels' in self.data and self.git_provider.is_supported("get_labels"): self.data.pop('labels') if not get_settings().pr_description.enable_pr_type: self.data.pop('type') - for key, value in self.data.items(): - markdown_text += f"## **{key}**\n\n" - markdown_text += f"{value}\n\n" # Remove the 'PR Title' key from the dictionary ai_title = self.data.pop('title', self.vars["title"]) @@ -579,6 +576,10 @@ class PRDescription: pr_body, changes_walkthrough = "", "" pr_file_changes = [] for idx, (key, value) in enumerate(self.data.items()): + if key == 'changes_diagram': + pr_body += f"### {PRDescriptionHeader.DIAGRAM_WALKTHROUGH.value}\n\n" + pr_body += f"{value}\n\n" + continue if key == 'pr_files': value = self.file_label_dict else: @@ -597,9 +598,15 @@ class PRDescription: pr_body += f'- `{filename}`: {description}\n' if self.git_provider.is_supported("gfm_markdown"): pr_body += "\n" - elif 'pr_files' in key.lower() and get_settings().pr_description.enable_semantic_files_types: - changes_walkthrough, pr_file_changes = self.process_pr_files_prediction(changes_walkthrough, value) - changes_walkthrough = f"{PRDescriptionHeader.CHANGES_WALKTHROUGH.value}\n{changes_walkthrough}" + elif 'pr_files' in key.lower() and get_settings().pr_description.enable_semantic_files_types: # 'File Walkthrough' section + changes_walkthrough_table, pr_file_changes = self.process_pr_files_prediction(changes_walkthrough, value) + if get_settings().pr_description.get('file_table_collapsible_open_by_default', False): + initial_status = " open" + else: + initial_status = "" + changes_walkthrough = f"

{PRDescriptionHeader.FILE_WALKTHROUGH.value}

\n\n" + changes_walkthrough += f"{changes_walkthrough_table}\n\n" + changes_walkthrough += "\n\n" elif key.lower().strip() == 'description': if isinstance(value, list): value = ', '.join(v.rstrip() for v in value) @@ -633,14 +640,18 @@ class PRDescription: artifact={"file": file}) continue filename = file['filename'].replace("'", "`").replace('"', '`') - changes_summary = file.get('changes_summary', "").strip() + changes_summary = file.get('changes_summary', "") + if not changes_summary: + get_logger().warning(f"Empty changes summary in file label dict, skipping file", + artifact={"file": file}) + changes_summary = changes_summary.strip() changes_title = file['changes_title'].strip() label = file.get('label').strip().lower() if label not in file_label_dict: file_label_dict[label] = [] file_label_dict[label].append((filename, changes_title, changes_summary)) except Exception as e: - get_logger().error(f"Error preparing file label dict {self.pr_id}: {e}") + get_logger().exception(f"Error preparing file label dict {self.pr_id}") pass return file_label_dict @@ -720,7 +731,7 @@ class PRDescription: pr_body += """""" except Exception as e: - get_logger().error(f"Error processing PR files to markdown {self.pr_id}: {str(e)}") + get_logger().error(f"Error processing pr files to markdown {self.pr_id}: {str(e)}") pass return pr_body, pr_comments @@ -776,14 +787,21 @@ def insert_br_after_x_chars(text: str, x=70): if count_chars_without_html(text) < x: return text + is_list = text.lstrip().startswith(("- ", "* ")) + # replace odd instances of ` with and even instances of ` with text = replace_code_tags(text) - # convert list items to
  • - if text.startswith("- ") or text.startswith("* "): - text = "
  • " + text[2:] - text = text.replace("\n- ", '
  • ').replace("\n - ", '
  • ') - text = text.replace("\n* ", '
  • ').replace("\n * ", '
  • ') + # convert list items to
  • only if the text is identified as a list + if is_list: + # To handle lists that start with indentation + leading_whitespace = text[:len(text) - len(text.lstrip())] + body = text.lstrip() + body = "
  • " + body[2:] + text = leading_whitespace + body + + text = text.replace("\n- ", '
  • ').replace("\n - ", '
  • ') + text = text.replace("\n* ", '
  • ').replace("\n * ", '
  • ') # convert new lines to
    text = text.replace("\n", '
    ') @@ -823,7 +841,13 @@ def insert_br_after_x_chars(text: str, x=70): is_inside_code = True if "" in word: is_inside_code = False - return ''.join(new_text).strip() + + processed_text = ''.join(new_text).strip() + + if is_list: + processed_text = f"
      {processed_text}
    " + + return processed_text def replace_code_tags(text): From e8c73e7baaf979047405814445a36503fd2a7ae2 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 18 Jul 2025 08:54:52 +0300 Subject: [PATCH 21/42] refactor(utils): improve file walkthrough parsing with regex and better error handling --- pr_agent/algo/utils.py | 43 +++++++--- tests/unittest/test_convert_to_markdown.py | 95 +++++++++++----------- 2 files changed, 80 insertions(+), 58 deletions(-) diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index eaa8ce47..9b924554 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -1285,14 +1285,35 @@ def process_description(description_full: str) -> Tuple[str, List]: if not description_full: return "", [] - description_split = description_full.split(PRDescriptionHeader.FILE_WALKTHROUGH.value) - base_description_str = description_split[0] - changes_walkthrough_str = "" - files = [] - if len(description_split) > 1: - changes_walkthrough_str = description_split[1] + # description_split = description_full.split(PRDescriptionHeader.FILE_WALKTHROUGH.value) + if PRDescriptionHeader.FILE_WALKTHROUGH.value in description_full: + try: + # FILE_WALKTHROUGH are presented in a collapsible section in the description + regex_pattern = r'\s*\s*

    \s*' + re.escape(PRDescriptionHeader.FILE_WALKTHROUGH.value) + r'\s*

    \s*
    ' + description_split = re.split(regex_pattern, description_full, maxsplit=1, flags=re.DOTALL) + + # If the regex pattern is not found, fallback to the previous method + if len(description_split) == 1: + get_logger().debug("Could not find regex pattern for file walkthrough, falling back to simple split") + description_split = description_full.split(PRDescriptionHeader.FILE_WALKTHROUGH.value, 1) + except Exception as e: + get_logger().warning(f"Failed to split description using regex, falling back to simple split: {e}") + description_split = description_full.split(PRDescriptionHeader.FILE_WALKTHROUGH.value, 1) + + if len(description_split) < 2: + get_logger().error("Failed to split description into base and changes walkthrough", artifact={'description': description_full}) + return description_full.strip(), [] + + base_description_str = description_split[0].strip() + changes_walkthrough_str = "" + files = [] + if len(description_split) > 1: + changes_walkthrough_str = description_split[1] + else: + get_logger().debug("No changes walkthrough found") else: - get_logger().debug("No changes walkthrough found") + base_description_str = description_full.strip() + return base_description_str, [] try: if changes_walkthrough_str: @@ -1315,18 +1336,20 @@ def process_description(description_full: str) -> Tuple[str, List]: try: if isinstance(file_data, tuple): file_data = file_data[0] - pattern = r'
    \s*(.*?)\s*
    (.*?).*?
    \s*
    \s*(.*?)\s*
  • (.*?)
  • ' + pattern = r'
    \s*(.*?)\s*
    (.*?).*?
    \s*
    \s*(.*?)\s*(?:
  • |•)(.*?)
  • ' res = re.search(pattern, file_data, re.DOTALL) if not res or res.lastindex != 4: pattern_back = r'
    \s*(.*?)
    (.*?).*?
    \s*
    \s*(.*?)\n\n\s*(.*?)
    ' res = re.search(pattern_back, file_data, re.DOTALL) if not res or res.lastindex != 4: - pattern_back = r'
    \s*(.*?)\s*
    (.*?).*?
    \s*
    \s*(.*?)\s*-\s*(.*?)\s*
    ' # looking for hyphen ('- ') + pattern_back = r'
    \s*(.*?)\s*
    (.*?).*?
    \s*
    \s*(.*?)\s*-\s*(.*?)\s*
    ' # looking for hypen ('- ') res = re.search(pattern_back, file_data, re.DOTALL) if res and res.lastindex == 4: short_filename = res.group(1).strip() short_summary = res.group(2).strip() long_filename = res.group(3).strip() + if long_filename.endswith('
      '): + long_filename = long_filename[:-4].strip() long_summary = res.group(4).strip() long_summary = long_summary.replace('
      *', '\n*').replace('
      ','').replace('\n','
      ') long_summary = h.handle(long_summary).strip() @@ -1345,7 +1368,7 @@ def process_description(description_full: str) -> Tuple[str, List]: if '...' in file_data: pass # PR with many files. some did not get analyzed else: - get_logger().error(f"Failed to parse description", artifact={'description': file_data}) + get_logger().warning(f"Failed to parse description", artifact={'description': file_data}) except Exception as e: get_logger().exception(f"Failed to process description: {e}", artifact={'description': file_data}) diff --git a/tests/unittest/test_convert_to_markdown.py b/tests/unittest/test_convert_to_markdown.py index 84e01942..187ea4a8 100644 --- a/tests/unittest/test_convert_to_markdown.py +++ b/tests/unittest/test_convert_to_markdown.py @@ -51,7 +51,7 @@ class TestConvertToMarkdown: input_data = {'review': { 'estimated_effort_to_review_[1-5]': '1, because the changes are minimal and straightforward, focusing on a single functionality addition.\n', 'relevant_tests': 'No\n', 'possible_issues': 'No\n', 'security_concerns': 'No\n'}} - + expected_output = textwrap.dedent(f"""\ {PRReviewHeader.REGULAR.value} 🔍 @@ -67,12 +67,12 @@ class TestConvertToMarkdown: """) assert convert_to_markdown_v2(input_data).strip() == expected_output.strip() - + def test_simple_dictionary_input_without_gfm_supported(self): input_data = {'review': { 'estimated_effort_to_review_[1-5]': '1, because the changes are minimal and straightforward, focusing on a single functionality addition.\n', 'relevant_tests': 'No\n', 'possible_issues': 'No\n', 'security_concerns': 'No\n'}} - + expected_output = textwrap.dedent("""\ ## PR Reviewer Guide 🔍 @@ -89,74 +89,74 @@ class TestConvertToMarkdown: """) assert convert_to_markdown_v2(input_data, gfm_supported=False).strip() == expected_output.strip() - + def test_key_issues_to_review(self): input_data = {'review': { 'key_issues_to_review': [ { - 'relevant_file' : 'src/utils.py', - 'issue_header' : 'Code Smell', - 'issue_content' : 'The function is too long and complex.', + 'relevant_file': 'src/utils.py', + 'issue_header': 'Code Smell', + 'issue_content': 'The function is too long and complex.', 'start_line': 30, 'end_line': 50, } ] }} mock_git_provider = Mock() - reference_link = 'https://github.com/qodo/pr-agent/pull/1/files#diff-hashvalue-R174' + reference_link = 'https://github.com/qodo/pr-agent/pull/1/files#diff-hashvalue-R174' mock_git_provider.get_line_link.return_value = reference_link expected_output = textwrap.dedent(f"""\ ## PR Reviewer Guide 🔍 - + Here are some key observations to aid the review process: - +
      ⚡ Recommended focus areas for review

      - + Code Smell
      The function is too long and complex. - +
      """) - + assert convert_to_markdown_v2(input_data, git_provider=mock_git_provider).strip() == expected_output.strip() mock_git_provider.get_line_link.assert_called_with('src/utils.py', 30, 50) - + def test_ticket_compliance(self): input_data = {'review': { 'ticket_compliance_check': [ { 'ticket_url': 'https://example.com/ticket/123', - 'ticket_requirements': '- Requirement 1\n- Requirement 2\n', - 'fully_compliant_requirements': '- Requirement 1\n- Requirement 2\n', + 'ticket_requirements': '- Requirement 1\n- Requirement 2\n', + 'fully_compliant_requirements': '- Requirement 1\n- Requirement 2\n', 'not_compliant_requirements': '', 'requires_further_human_verification': '', } ] }} - + expected_output = textwrap.dedent("""\ ## PR Reviewer Guide 🔍 - + Here are some key observations to aid the review process: - +
      - + **🎫 Ticket compliance analysis ✅** - - - + + + **[123](https://example.com/ticket/123) - Fully compliant** - + Compliant requirements: - + - Requirement 1 - Requirement 2 - - - + + +
      """) @@ -179,43 +179,43 @@ class TestConvertToMarkdown: ], 'title': 'Bug Fix', } - ] - } + ] + } } expected_output = textwrap.dedent("""\ ## PR Reviewer Guide 🔍 - + Here are some key observations to aid the review process:
      🔀 Multiple PR themes

      - +
      Sub-PR theme: Refactoring - + ___ - + Relevant files: - + - src/file1.py - src/file2.py ___ - +
      - +
      Sub-PR theme: Bug Fix - + ___ - + Relevant files: - + - src/file3.py ___ - +
      - +
      """) @@ -228,7 +228,6 @@ class TestConvertToMarkdown: expected_output = '' - assert convert_to_markdown_v2(input_data).strip() == expected_output.strip() def test_dictionary_with_empty_dictionaries(self): @@ -236,16 +235,16 @@ class TestConvertToMarkdown: expected_output = '' - assert convert_to_markdown_v2(input_data).strip() == expected_output.strip() + class TestBR: def test_br1(self): file_change_description = '- Imported `FilePatchInfo` and `EDIT_TYPE` from `pr_agent.algo.types` instead of `pr_agent.git_providers.git_provider`.' file_change_description_br = insert_br_after_x_chars(file_change_description) - expected_output = ('
    • Imported FilePatchInfo and EDIT_TYPE from ' + expected_output = ('
      • Imported FilePatchInfo and EDIT_TYPE from ' 'pr_agent.algo.types instead
        of ' - 'pr_agent.git_providers.git_provider.') + 'pr_agent.git_providers.git_provider.
      ') assert file_change_description_br == expected_output # print("-----") # print(file_change_description_br) @@ -255,9 +254,9 @@ class TestBR: '- Created a - new -class `ColorPaletteResourcesCollection ColorPaletteResourcesCollection ' 'ColorPaletteResourcesCollection ColorPaletteResourcesCollection`') file_change_description_br = insert_br_after_x_chars(file_change_description) - expected_output = ('
    • Created a - new -class ColorPaletteResourcesCollection
      ' + expected_output = ('
      • Created a - new -class ColorPaletteResourcesCollection
        ' 'ColorPaletteResourcesCollection ColorPaletteResourcesCollection ' - '
        ColorPaletteResourcesCollection') + '

        ColorPaletteResourcesCollection
      ') assert file_change_description_br == expected_output # print("-----") # print(file_change_description_br) From 6179eeca58d2966c11723dc8b00c313e167aab9c Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 18 Jul 2025 09:14:52 +0300 Subject: [PATCH 22/42] fix(pr_description): fix template syntax errors and improve formatting in prompts --- docs/docs/tools/describe.md | 3 +++ pr_agent/settings/pr_description_prompts.toml | 24 +++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/docs/docs/tools/describe.md b/docs/docs/tools/describe.md index d929679c..74408918 100644 --- a/docs/docs/tools/describe.md +++ b/docs/docs/tools/describe.md @@ -124,6 +124,9 @@ This option is enabled by default via the `pr_description.enable_pr_diagram` par enable_semantic_files_types If set to true, "Changes walkthrough" section will be generated. Default is true. + + file_table_collapsible_open_by_default + If set to true, the file list in the "Changes walkthrough" section will be open by default. If set to false, it will be closed by default. Default is false. collapsible_file_list If set to true, the file list in the "Changes walkthrough" section will be collapsible. If set to "adaptive", the file list will be collapsible only if there are more than 8 files. Default is "adaptive". diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index cc789c94..ba51e1ec 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -48,8 +48,8 @@ class PRDescription(BaseModel): description: str = Field(description="summarize the PR changes in up to four bullet points, each up to 8 words. For large PRs, add sub-bullets if needed. Order bullets by importance, with each bullet highlighting a key change group.") title: str = Field(description="a concise and descriptive title that captures the PR's main theme") {%- if enable_pr_diagram %} - changes_diagram: str = Field(description="a horizontal diagram that represents the main PR changes, in the format of a valid mermaid LR flowchart. The diagram should be concise and easy to read. Leave empty if no diagram is relevant. To create robust Mermaid diagrams, follow this two-step process: (1) Declare the nodes: nodeID[\"node description\"]. (2) Then define the links: nodeID1 -- \"link text\" --> nodeID2. Node description must always be surrounded with quotation marks.") -{%- endif %} + changes_diagram: str = Field(description='a horizontal diagram that represents the main PR changes, in the format of a valid mermaid LR flowchart. The diagram should be concise and easy to read. Leave empty if no diagram is relevant. To create robust Mermaid diagrams, follow this two-step process: (1) Declare the nodes: nodeID["node description"]. (2) Then define the links: nodeID1 -- "link text" --> nodeID2. Node description must always be surrounded with double quotation marks') +'{%- endif %} {%- if enable_semantic_files_types %} pr_files: List[FileDescription] = Field(max_items=20, description="a list of all the files that were changed in the PR, and summary of their changes. Each file must be analyzed regardless of change size.") {%- endif %} @@ -67,11 +67,11 @@ description: | title: | ... {%- if enable_pr_diagram %} - changes_diagram: | - ```mermaid - flowchart LR - ... - ``` +changes_diagram: | + ```mermaid + flowchart LR + ... + ``` {%- endif %} {%- if enable_semantic_files_types %} pr_files: @@ -155,11 +155,11 @@ description: | title: | ... {%- if enable_pr_diagram %} - changes_diagram: | - ```mermaid - flowchart LR - ... - ``` +changes_diagram: | + ```mermaid + flowchart LR + ... + ``` {%- endif %} {%- if enable_semantic_files_types %} pr_files: From 8906a81a2e346d0275f332ad64d0b48d52bf4329 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 18 Jul 2025 09:17:14 +0300 Subject: [PATCH 23/42] fix(docs): fix table row structure in describe tool documentation --- docs/docs/tools/describe.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/docs/tools/describe.md b/docs/docs/tools/describe.md index 74408918..1ade6c56 100644 --- a/docs/docs/tools/describe.md +++ b/docs/docs/tools/describe.md @@ -124,9 +124,10 @@ This option is enabled by default via the `pr_description.enable_pr_diagram` par enable_semantic_files_types If set to true, "Changes walkthrough" section will be generated. Default is true. - + file_table_collapsible_open_by_default If set to true, the file list in the "Changes walkthrough" section will be open by default. If set to false, it will be closed by default. Default is false. + collapsible_file_list If set to true, the file list in the "Changes walkthrough" section will be collapsible. If set to "adaptive", the file list will be collapsible only if there are more than 8 files. Default is "adaptive". From 7cc4206b702821c1687ac41275e26764cd425959 Mon Sep 17 00:00:00 2001 From: Tal Date: Fri, 18 Jul 2025 09:20:41 +0300 Subject: [PATCH 24/42] Update pr_agent/tools/pr_description.py Co-authored-by: qodo-merge-for-open-source[bot] <189517486+qodo-merge-for-open-source[bot]@users.noreply.github.com> --- pr_agent/tools/pr_description.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index c9ecdbe2..5abea388 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -644,6 +644,7 @@ class PRDescription: if not changes_summary: get_logger().warning(f"Empty changes summary in file label dict, skipping file", artifact={"file": file}) + continue changes_summary = changes_summary.strip() changes_title = file['changes_title'].strip() label = file.get('label').strip().lower() From 9773afe15501abb6809bdb4e48c03f247fbe75e4 Mon Sep 17 00:00:00 2001 From: ofir-frd <85901822+ofir-frd@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:31:16 +0300 Subject: [PATCH 25/42] docs: minor fixes in the compliance.md file --- docs/docs/tools/compliance.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/docs/tools/compliance.md b/docs/docs/tools/compliance.md index f55c6be5..8512cf22 100644 --- a/docs/docs/tools/compliance.md +++ b/docs/docs/tools/compliance.md @@ -2,7 +2,7 @@ ## Overview -The `compliance` tool performs comprehensive compliance checks on PR code changes, validating them against security standards, ticket requirements, and custom organizational compliance checklists, thereby helping teams maintain consistent code quality and security practices while ensuring that development work aligns with business requirements. +The `compliance` tool performs comprehensive compliance checks on PR code changes, validating them against security standards, ticket requirements, and custom organizational compliance checklists, thereby helping teams, enterprises, and agents maintain consistent code quality and security practices while ensuring that development work aligns with business requirements. === "Fully Compliant" ![compliance_overview](https://codium.ai/images/pr_agent/compliance_full.png){width=256} @@ -67,7 +67,7 @@ Scans for security vulnerabilities and potential exploits in the PR code changes - **Verified Security Concerns** 🔴: Clear security vulnerabilities that require immediate attention - **Possible Security Risks** ⚪: Potential security issues that need human verification -- **No Security Concerns** 🟢: No security vulnerabilities were detected +- **No Security Concerns** 🟢: No security vulnerabilities detected Examples of security issues: @@ -103,7 +103,7 @@ Validates against an organization-specific compliance checklist: ### Setting Up Custom Compliance -Each compliance is defined in a YAML file, as follows: +Each compliance is defined in a YAML file as follows: - `title`: Used to provide a clear name for the compliance - `compliance_label`: Used to automatically generate labels for non-compliance issues - `objective`, `success_criteria`, and `failure_criteria`: These fields are used to clearly define what constitutes compliance @@ -122,7 +122,7 @@ Each compliance is defined in a YAML file, as follows: ... ``` -???+ tip "Writing effective compliance checklist" +???+ tip "Writing effective compliance checklists" - Avoid overly complex or subjective compliances that are hard to verify - Keep compliances focused on security, business requirements, and critical standards - Use clear, actionable language that developers can understand @@ -135,7 +135,7 @@ Qodo Merge supports hierarchical compliance checklists using a dedicated global #### Setting up global hierarchical compliance -1\. Create a new repository named `pr-agent-settings` in your organization/workspace. +1\. Create a new repository named `pr-agent-settings` in your organization or workspace. 2\. Build the folder hierarchy in your `pr-agent-settings` repository: @@ -153,14 +153,14 @@ pr-agent-settings/ ├── qodo-merge/ # For standalone repositories │ └── pr_compliance_checklist.yaml └── qodo-monorepo/ # For monorepo-specific compliance - ├── pr_compliance_checklist.yaml # Root level monorepo compliance + ├── pr_compliance_checklist.yaml # Root-level monorepo compliance ├── qodo-github/ # Subproject compliance │ └── pr_compliance_checklist.yaml └── qodo-gitlab/ # Another subproject └── pr_compliance_checklist.yaml ``` -3\. Define the metadata file `metadata.yaml` in the `pr-agent-settings` root: +3\. Define the metadata file `metadata.yaml` in the root of `pr-agent-settings`: ```yaml # Standalone repos @@ -225,7 +225,7 @@ enable_global_pr_compliance = true - + @@ -233,11 +233,11 @@ enable_global_pr_compliance = true - + - + @@ -245,11 +245,11 @@ enable_global_pr_compliance = true - + - +
      extra_instructionsOptional extra instructions for the tool. For example: "focus on the changes in the file X. Ignore changes in ...". Default is empty string.Optional extra instructions for the tool. For example: "Ensure that all error-handling paths in the code contain appropriate logging statements". Default is empty string.
      persistent_comment
      enable_user_defined_compliance_labelsIf set to true, the tool will add labels for custom compliance violations. Default is true.If set to true, the tool will add the label `Failed compliance check` for custom compliance violations. Default is true.
      enable_estimate_effort_to_reviewIf set to true, the tool will estimate the effort required to review the PR (1-5 scale). Default is true.If set to true, the tool will estimate the effort required to review the PR (1-5 scale) as a label. Default is true.
      enable_todo_scan
      enable_update_pr_compliance_checkboxIf set to true, the tool will add an update checkbox to refresh compliance status. Default is true.If set to true, the tool will add an update checkbox to refresh compliance status following push events. Default is true.
      enable_help_textIf set to true, the tool will display a help text in the comment. Default is false.If set to true, the tool will display help text in the comment. Default is false.
      @@ -262,7 +262,7 @@ enable_global_pr_compliance = true enable_compliance_labels_security - If set to true, the tool will add security-related labels to the PR. Default is true. + If set to true, the tool will add a `Possible security concern` label to the PR when security-related concerns are detected. Default is true. From 65e71cb2ee94eaecec3977dbcd0f19175c232206 Mon Sep 17 00:00:00 2001 From: ofir-frd <85901822+ofir-frd@users.noreply.github.com> Date: Fri, 18 Jul 2025 17:07:50 +0300 Subject: [PATCH 26/42] docs: fix formatting inside the collapsible compliance priority section in Compliance.md --- docs/docs/tools/compliance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/compliance.md b/docs/docs/tools/compliance.md index 8512cf22..ea57184f 100644 --- a/docs/docs/tools/compliance.md +++ b/docs/docs/tools/compliance.md @@ -209,7 +209,7 @@ enable_global_pr_compliance = true 1.3 If no mapping exists, it falls back to the global compliance checklists - 2. **Fallback**: Local repository wiki `pr_compliance_checklist.yaml` file: + 2\. **Fallback**: Local repository wiki `pr_compliance_checklist.yaml` file: 2.1 Used when global compliance checklists are not found or configured From b01a2b5f4af1d7b5bbbad70129acafce00f560dc Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 20 Jul 2025 08:23:53 +0300 Subject: [PATCH 27/42] docs: add num_best_practice_suggestions parameter to improve tool configuration table --- docs/docs/tools/improve.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index bdc83630..b9ea8417 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -598,6 +598,10 @@ Note: Chunking is primarily relevant for large PRs. For most PRs (up to 600 line num_code_suggestions_per_chunk Number of code suggestions provided by the 'improve' tool, per chunk. Default is 3. + + num_best_practice_suggestions 💎 + Number of code suggestions provided by the 'improve' tool for best practices. Default is 1. + max_number_of_calls Maximum number of chunks. Default is 3. From 7251e6df963b5f64c941f28ecf15cddbb1c6012e Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 20 Jul 2025 08:26:07 +0300 Subject: [PATCH 28/42] docs: add num_best_practice_suggestions parameter to improve tool configuration table --- pr_agent/settings/configuration.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 3613d136..37ffbb7a 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -152,7 +152,8 @@ new_score_mechanism_th_high=9 new_score_mechanism_th_medium=7 # params for '/improve --extended' mode auto_extended_mode=true -num_code_suggestions_per_chunk=4 +num_code_suggestions_per_chunk=3 +num_best_practice_suggestions=1 # 💎 max_number_of_calls = 3 parallel_calls = true From f42dc28a554f1578d8524f560783c7ee32fcaa29 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 20 Jul 2025 11:56:55 +0300 Subject: [PATCH 29/42] feat(config): enhance ignore_pr_authors to support regex patterns --- docs/docs/usage-guide/additional_configurations.md | 2 +- pr_agent/servers/bitbucket_app.py | 2 +- pr_agent/servers/github_app.py | 2 +- pr_agent/servers/gitlab_webhook.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/usage-guide/additional_configurations.md b/docs/docs/usage-guide/additional_configurations.md index 17b49cf9..ef18eb44 100644 --- a/docs/docs/usage-guide/additional_configurations.md +++ b/docs/docs/usage-guide/additional_configurations.md @@ -246,7 +246,7 @@ To supplement the automatic bot detection, you can manually specify users to ign ignore_pr_authors = ["my-special-bot-user", ...] ``` -Where the `ignore_pr_authors` is a list of usernames that you want to ignore. +Where the `ignore_pr_authors` is a regex list of usernames that you want to ignore. !!! note There is one specific case where bots will receive an automatic response - when they generated a PR with a _failed test_. In that case, the [`ci_feedback`](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) tool will be invoked. diff --git a/pr_agent/servers/bitbucket_app.py b/pr_agent/servers/bitbucket_app.py index 0d75d143..7c751bd5 100644 --- a/pr_agent/servers/bitbucket_app.py +++ b/pr_agent/servers/bitbucket_app.py @@ -139,7 +139,7 @@ def should_process_pr_logic(data) -> bool: # logic to ignore PRs from specific users ignore_pr_users = get_settings().get("CONFIG.IGNORE_PR_AUTHORS", []) if ignore_pr_users and sender: - if sender in ignore_pr_users: + if any(re.search(regex, sender) for regex in ignore_pr_users): get_logger().info(f"Ignoring PR from user '{sender}' due to 'config.ignore_pr_authors' setting") return False diff --git a/pr_agent/servers/github_app.py b/pr_agent/servers/github_app.py index 36db3c69..b94b79e3 100644 --- a/pr_agent/servers/github_app.py +++ b/pr_agent/servers/github_app.py @@ -270,7 +270,7 @@ def should_process_pr_logic(body) -> bool: # logic to ignore PRs from specific users ignore_pr_users = get_settings().get("CONFIG.IGNORE_PR_AUTHORS", []) if ignore_pr_users and sender: - if sender in ignore_pr_users: + if any(re.search(regex, sender) for regex in ignore_pr_users): get_logger().info(f"Ignoring PR from user '{sender}' due to 'config.ignore_pr_authors' setting") return False diff --git a/pr_agent/servers/gitlab_webhook.py b/pr_agent/servers/gitlab_webhook.py index 60f13501..69187530 100644 --- a/pr_agent/servers/gitlab_webhook.py +++ b/pr_agent/servers/gitlab_webhook.py @@ -125,7 +125,7 @@ def should_process_pr_logic(data) -> bool: # logic to ignore PRs from specific users ignore_pr_users = get_settings().get("CONFIG.IGNORE_PR_AUTHORS", []) if ignore_pr_users and sender: - if sender in ignore_pr_users: + if any(re.search(regex, sender) for regex in ignore_pr_users): get_logger().info(f"Ignoring PR from user '{sender}' due to 'config.ignore_pr_authors' settings") return False From 730fa665943f376a435e38a400f6c38a103de80b Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Sun, 20 Jul 2025 18:15:21 +0300 Subject: [PATCH 30/42] add docs --- .../core-abilities/fetching_ticket_context.md | 2 +- docs/docs/tools/index.md | 1 + docs/docs/tools/pr_to_ticket.md | 85 +++++++++++++++++++ docs/mkdocs.yml | 1 + 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 docs/docs/tools/pr_to_ticket.md diff --git a/docs/docs/core-abilities/fetching_ticket_context.md b/docs/docs/core-abilities/fetching_ticket_context.md index 63cd65f4..1dd30c57 100644 --- a/docs/docs/core-abilities/fetching_ticket_context.md +++ b/docs/docs/core-abilities/fetching_ticket_context.md @@ -104,7 +104,7 @@ Installation steps: 2. Click on the Connect **Jira Cloud** button to connect the Jira Cloud app 3. Click the `accept` button.
      -![Jira Cloud App Installation](https://www.qodo.ai/images/pr_agent/jira_app_installation1.png){width=384} +![Jira Cloud App Installation](https://www.qodo.ai/images/pr_agent/jira_app_installation2.png){width=384} 4. After installing the app, you will be redirected to the Qodo Merge registration page. and you will see a success message.
      ![Jira Cloud App success message](https://www.qodo.ai/images/pr_agent/jira_app_success.png){width=384} diff --git a/docs/docs/tools/index.md b/docs/docs/tools/index.md index cce65834..5efa2d30 100644 --- a/docs/docs/tools/index.md +++ b/docs/docs/tools/index.md @@ -15,6 +15,7 @@ Here is a list of Qodo Merge tools, each with a dedicated page that explains how | **💎 [Analyze (`/analyze`](./analyze.md))** | Identify code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component | | **💎 [CI Feedback (`/checks ci_job`](./ci_feedback.md))** | Automatically generates feedback and analysis for a failed CI job | | **💎 [Compliance (`/compliance`](./compliance.md))** | Comprehensive compliance checks for security, ticket requirements, and custom organizational rules | +| **💎 [PR to Ticket (`/create_ticket`](./pr_to_ticket.md))** | Generates ticket in the ticket tracking systems (Jira, Linear, or Git provider issues) based on PR content | | **💎 [Custom Prompt (`/custom_prompt`](./custom_prompt.md))** | Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user | | **💎 [Generate Custom Labels (`/generate_labels`](./custom_labels.md))** | Generates custom labels for the PR, based on specific guidelines defined by the user | | **💎 [Generate Tests (`/test`](./test.md))** | Automatically generates unit tests for a selected component, based on the PR code changes | diff --git a/docs/docs/tools/pr_to_ticket.md b/docs/docs/tools/pr_to_ticket.md new file mode 100644 index 00000000..375af93d --- /dev/null +++ b/docs/docs/tools/pr_to_ticket.md @@ -0,0 +1,85 @@ +`Platforms supported: GitHub, GitLab, Bitbucket` + +## Overview +The `create_ticket` tool automatically generates tickets in ticket tracking systems (`Jira`, `Linear`, or `GitHub Issues`) based on PR content. + +It analyzes the PR's data (code changes, commit messages, and description) to create well-structured tickets that capture the essence of the development work, helping teams maintain traceability between code changes and project management systems. + +When a ticket is created, it appears in the PR description under an `Auto-created Ticket` section, complete with a link to the generated ticket. + +![auto_created_ticket_in_description](https://codium.ai/images/pr_agent/auto_created_ticket_in_description.png){width=256} + +!!! info "Pre-requisites" + - To use this tool you need to integrate your ticketing system with Qodo-merge, follow the [Ticket Compliance Documentation](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/). + - For Jira Cloud users, please re-integrate your connection through the [qodo merge integration page](https://app.qodo.ai/qodo-merge/integrations) to enable the `update` permission required for ticket creation + - You need to configure the project key in ticket corresponding to the repository where the PR is created. This is done by adding the `default_project_key`. + + ```toml + [pr_to_ticket] + default_project_key = "PROJECT_KEY" # e.g., "SCRUM" + ``` + +## Usage +there are 3 ways to use the `create_ticket` tool: + +1. [**Automatic Ticket Creation**](#automatic-ticket-creation) +2. [**Interactive Triggering via Compliance Tool**](#interactive-triggering-via-compliance-tool) +3. [**Manual Ticket Creation**](#manual-ticket-creation) + +### Automatic Ticket Creation +The tool can be configured to automatically create tickets when a PR is opened or updated and the PR does not already have a ticket associated with it. +This ensures that every code change is documented in the ticketing system without manual intervention. + +To configure automatic ticket creation, add the following to `.pr_agnet.toml`: + +```toml +[pr_description] +auto_create_ticket = true +``` + +### Interactive Triggering via Compliance Tool +The tool can be triggered interactively through a checkbox in the compliance tool. This allows users to create tickets as part of their PR Compliance Review workflow. + +![ticket creation via compliance tool](https://codium.ai/images/pr_agent/ticket_creation_from_compliance1.png){width=256} + +- After clicking the checkbox, the tool will create a ticket and will add/update the `PR Description` with a section called `Auto-created Ticket` with the link to the created ticket. +- Then you can click `update` in the `Ticket compliance` section in the `Compliance` tool + +![compliance_auto_created_ticket_final](https://codium.ai/images/pr_agent/compliance_auto_created_ticket_final.png){width=365} + +### Manual Ticket Creation +Users can manually trigger the ticket creation process from the PR interface. + +To trigger ticket creation manually, the user can call this tool from the PR comment: + +``` +/create_ticket +``` + +After triggering, the tool will create a ticket and will add/update the `PR Description` with a section called `Auto-created Ticket` with the link to the created ticket. + + +## Configuration + +## Configuration Options + +???+ example "Configuration" + + + + + + + + + + + + + + +
      default_project_key (required*)The default project key to use when creating tickets. This is required for the tool to create tickets in the ticketing system. Example: `SCRUM`.
      default_base_urlIf your organization have integrated to multiple ticketing systems, you can set the default base URL for the ticketing system. This will be used to create tickets in the default system. Example: `https://YOUR-ORG.atlassian.net`.
      fallback_to_git_provider_issuesIf set to `true`, the tool will create issues in the Git provider's issue tracker (GitHub) if the `default_project_key` is not configured in the repository configuration. Default is `false`.
      + + +## Helping Your Organization Meet SOC-2 Requirements +The `create_ticket` tool helps your organization satisfy SOC-2 compliance. By automatically creating tickets from PRs and establishing bidirectional links between them, it ensures every code change is traceable to its corresponding business requirement or task. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 746341a6..ff33d020 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -35,6 +35,7 @@ nav: - 💎 Analyze: 'tools/analyze.md' - 💎 CI Feedback: 'tools/ci_feedback.md' - 💎 Compliance: 'tools/compliance.md' + - 💎 PR to Ticket: 'tools/pr_to_ticket.md' - 💎 Custom Prompt: 'tools/custom_prompt.md' - 💎 Generate Labels: 'tools/custom_labels.md' - 💎 Generate Tests: 'tools/test.md' From 7f95e393617c6fc17b7265fd5ef9c84a11427312 Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Sun, 20 Jul 2025 19:08:42 +0300 Subject: [PATCH 31/42] add docs --- docs/docs/index.md | 33 +++++++++++++++++---------------- docs/docs/tools/compliance.md | 6 ++++++ docs/docs/tools/describe.md | 4 ++++ docs/docs/tools/pr_to_ticket.md | 2 ++ 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/docs/docs/index.md b/docs/docs/index.md index b4738d80..6f74e2a2 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -28,18 +28,19 @@ PR-Agent and Qodo Merge offer comprehensive pull request functionalities integra | | | GitHub | GitLab | Bitbucket | Azure DevOps | Gitea | | ----- |---------------------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|:-----:| -| [TOOLS](https://qodo-merge-docs.qodo.ai/tools/) | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | | +| [TOOLS](https://qodo-merge-docs.qodo.ai/tools/) | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | | | | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | | | | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | | -| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | | +| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | | | | [Add Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | | | | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | | | | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | | | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | -| | [Compliance](https://qodo-merge-docs.qodo.ai/tools/compliance/) 💎 | ✅ | ✅ | ✅ | | | +| | [Compliance](https://qodo-merge-docs.qodo.ai/tools/compliance/) 💎 | ✅ | ✅ | ✅ | | | +| | [PR to Ticket](https://qodo-merge-docs.qodo.ai/tools/pr_to_ticket/) 💎 | ✅ | ✅ | ✅ | | | | | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | | | | [Generate Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | | | [Generate Tests](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | | @@ -51,27 +52,27 @@ PR-Agent and Qodo Merge offer comprehensive pull request functionalities integra | | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | | | | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | | | | | | | | | -| [USAGE](https://qodo-merge-docs.qodo.ai/usage-guide/) | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [USAGE](https://qodo-merge-docs.qodo.ai/usage-guide/) | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | ✅ | | | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | | -| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ | ✅ | ✅ | ✅ | | +| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ | ✅ | ✅ | ✅ | | | | | | | | | | -| [CORE](https://qodo-merge-docs.qodo.ai/core-abilities/) | [Adaptive and token-aware file patch fitting](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | +| [CORE](https://qodo-merge-docs.qodo.ai/core-abilities/) | [Adaptive and token-aware file patch fitting](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | | | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | | | | [Chat on code suggestions](https://qodo-merge-docs.qodo.ai/core-abilities/chat_on_code_suggestions/) | ✅ | ✅ | | | | -| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | | -| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | | +| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | | +| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | | | | [Fetching ticket context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) | ✅ | ✅ | ✅ | | | | | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | | | | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | | | | [Incremental Update 💎](https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update/) | ✅ | | | | | | | [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) | ✅ | ✅ | | | | -| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | | -| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | | -| | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | +| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | | +| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | | +| | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | | | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | | | [RAG context enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) | ✅ | | ✅ | | | -| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | | +| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | | | | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | | !!! note "💎 means Qodo Merge only" All along the documentation, 💎 marks a feature available only in [Qodo Merge](https://www.codium.ai/pricing/){:target="_blank"}, and not in the open-source version. diff --git a/docs/docs/tools/compliance.md b/docs/docs/tools/compliance.md index ea57184f..38251af5 100644 --- a/docs/docs/tools/compliance.md +++ b/docs/docs/tools/compliance.md @@ -83,6 +83,12 @@ Examples of security issues: ???+ tip "How to set up ticket compliance" Follow the guide on how to set up [ticket compliance](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) with Qodo Merge. +???+ tip "Auto-create ticket" + Follow this [guide](https://qodo-merge-docs.qodo.ai/tools/pr_to_ticket/) to learn how to enable triggering `create tickets` based on PR content. + + ![ticket creation via compliance tool](https://codium.ai/images/pr_agent/ticket_creation_from_compliance1.png){width=256} + + Validates that PR changes fulfill the requirements specified in linked tickets: - **Fully Compliant** 🟢: All ticket requirements are satisfied diff --git a/docs/docs/tools/describe.md b/docs/docs/tools/describe.md index 1ade6c56..70cc58bf 100644 --- a/docs/docs/tools/describe.md +++ b/docs/docs/tools/describe.md @@ -144,6 +144,10 @@ This option is enabled by default via the `pr_description.enable_pr_diagram` par enable_pr_diagram If set to true, the tool will generate a horizontal Mermaid flowchart summarizing the main pull request changes. This field remains empty if not applicable. Default is true. + + auto_create_ticket + If set to true, this will automatically create a ticket in the ticketing system when a PR is opened. Default is false. + ## Inline file summary 💎 diff --git a/docs/docs/tools/pr_to_ticket.md b/docs/docs/tools/pr_to_ticket.md index 375af93d..65603f32 100644 --- a/docs/docs/tools/pr_to_ticket.md +++ b/docs/docs/tools/pr_to_ticket.md @@ -38,6 +38,8 @@ auto_create_ticket = true ``` ### Interactive Triggering via Compliance Tool +`Supported only in Github and Gitlab` + The tool can be triggered interactively through a checkbox in the compliance tool. This allows users to create tickets as part of their PR Compliance Review workflow. ![ticket creation via compliance tool](https://codium.ai/images/pr_agent/ticket_creation_from_compliance1.png){width=256} From 5e555d09c70c8c3fd9fbbac15c2c2a85e01c2224 Mon Sep 17 00:00:00 2001 From: Hussam Lawen Date: Sun, 20 Jul 2025 19:18:59 +0300 Subject: [PATCH 32/42] Update docs/docs/tools/pr_to_ticket.md Co-authored-by: qodo-merge-for-open-source[bot] <189517486+qodo-merge-for-open-source[bot]@users.noreply.github.com> --- docs/docs/tools/pr_to_ticket.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/pr_to_ticket.md b/docs/docs/tools/pr_to_ticket.md index 65603f32..06bce869 100644 --- a/docs/docs/tools/pr_to_ticket.md +++ b/docs/docs/tools/pr_to_ticket.md @@ -30,7 +30,7 @@ there are 3 ways to use the `create_ticket` tool: The tool can be configured to automatically create tickets when a PR is opened or updated and the PR does not already have a ticket associated with it. This ensures that every code change is documented in the ticketing system without manual intervention. -To configure automatic ticket creation, add the following to `.pr_agnet.toml`: +To configure automatic ticket creation, add the following to `.pr_agent.toml`: ```toml [pr_description] From fffbee5b34c334e93336399a9e3fee502e9c7ccd Mon Sep 17 00:00:00 2001 From: Hussam Lawen Date: Sun, 20 Jul 2025 19:21:17 +0300 Subject: [PATCH 33/42] Update docs/docs/tools/pr_to_ticket.md Co-authored-by: qodo-merge-for-open-source[bot] <189517486+qodo-merge-for-open-source[bot]@users.noreply.github.com> --- docs/docs/tools/pr_to_ticket.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tools/pr_to_ticket.md b/docs/docs/tools/pr_to_ticket.md index 06bce869..aae228d0 100644 --- a/docs/docs/tools/pr_to_ticket.md +++ b/docs/docs/tools/pr_to_ticket.md @@ -69,8 +69,8 @@ After triggering, the tool will create a ticket and will add/update the `PR Desc - - + + From 93aaa59b2d573466f8fa1254319d23be0cec7ca5 Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Sun, 20 Jul 2025 20:38:54 +0300 Subject: [PATCH 34/42] reorder --- docs/docs/index.md | 6 +++--- docs/docs/tools/index.md | 2 +- docs/docs/tools/pr_to_ticket.md | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/docs/index.md b/docs/docs/index.md index 6f74e2a2..8023dd4e 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -40,17 +40,17 @@ PR-Agent and Qodo Merge offer comprehensive pull request functionalities integra | | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | | | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | | | [Compliance](https://qodo-merge-docs.qodo.ai/tools/compliance/) 💎 | ✅ | ✅ | ✅ | | | -| | [PR to Ticket](https://qodo-merge-docs.qodo.ai/tools/pr_to_ticket/) 💎 | ✅ | ✅ | ✅ | | | | | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | | | | [Generate Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | | | [Generate Tests](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | | | | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | +| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | | +| | [PR to Ticket](https://qodo-merge-docs.qodo.ai/tools/pr_to_ticket/) 💎 | ✅ | ✅ | ✅ | | | | | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | | | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | | +| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | | | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | | | | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | | -| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | | -| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | | | | | | | | | | [USAGE](https://qodo-merge-docs.qodo.ai/usage-guide/) | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | ✅ | | | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | ✅ | diff --git a/docs/docs/tools/index.md b/docs/docs/tools/index.md index 5efa2d30..1c00fab6 100644 --- a/docs/docs/tools/index.md +++ b/docs/docs/tools/index.md @@ -15,12 +15,12 @@ Here is a list of Qodo Merge tools, each with a dedicated page that explains how | **💎 [Analyze (`/analyze`](./analyze.md))** | Identify code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component | | **💎 [CI Feedback (`/checks ci_job`](./ci_feedback.md))** | Automatically generates feedback and analysis for a failed CI job | | **💎 [Compliance (`/compliance`](./compliance.md))** | Comprehensive compliance checks for security, ticket requirements, and custom organizational rules | -| **💎 [PR to Ticket (`/create_ticket`](./pr_to_ticket.md))** | Generates ticket in the ticket tracking systems (Jira, Linear, or Git provider issues) based on PR content | | **💎 [Custom Prompt (`/custom_prompt`](./custom_prompt.md))** | Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user | | **💎 [Generate Custom Labels (`/generate_labels`](./custom_labels.md))** | Generates custom labels for the PR, based on specific guidelines defined by the user | | **💎 [Generate Tests (`/test`](./test.md))** | Automatically generates unit tests for a selected component, based on the PR code changes | | **💎 [Implement (`/implement`](./implement.md))** | Generates implementation code from review suggestions | | **💎 [Improve Component (`/improve_component component_name`](./improve_component.md))** | Generates code suggestions for a specific code component that changed in the PR | +| **💎 [PR to Ticket (`/create_ticket`](./pr_to_ticket.md))** | Generates ticket in the ticket tracking systems (Jira, Linear, or Git provider issues) based on PR content | | **💎 [Scan Repo Discussions (`/scan_repo_discussions`](./scan_repo_discussions.md))** | Generates `best_practices.md` file based on previous discussions in the repository | | **💎 [Similar Code (`/similar_code`](./similar_code.md))** | Retrieves the most similar code components from inside the organization's codebase, or from open-source code. | diff --git a/docs/docs/tools/pr_to_ticket.md b/docs/docs/tools/pr_to_ticket.md index aae228d0..0752d9c2 100644 --- a/docs/docs/tools/pr_to_ticket.md +++ b/docs/docs/tools/pr_to_ticket.md @@ -7,7 +7,7 @@ It analyzes the PR's data (code changes, commit messages, and description) to cr When a ticket is created, it appears in the PR description under an `Auto-created Ticket` section, complete with a link to the generated ticket. -![auto_created_ticket_in_description](https://codium.ai/images/pr_agent/auto_created_ticket_in_description.png){width=256} +![auto_created_ticket_in_description](https://codium.ai/images/pr_agent/auto_created_ticket_in_description.png){width=512} !!! info "Pre-requisites" - To use this tool you need to integrate your ticketing system with Qodo-merge, follow the [Ticket Compliance Documentation](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/). @@ -42,12 +42,12 @@ auto_create_ticket = true The tool can be triggered interactively through a checkbox in the compliance tool. This allows users to create tickets as part of their PR Compliance Review workflow. -![ticket creation via compliance tool](https://codium.ai/images/pr_agent/ticket_creation_from_compliance1.png){width=256} +![ticket creation via compliance tool](https://codium.ai/images/pr_agent/ticket_creation_from_compliance1.png){width=512} - After clicking the checkbox, the tool will create a ticket and will add/update the `PR Description` with a section called `Auto-created Ticket` with the link to the created ticket. - Then you can click `update` in the `Ticket compliance` section in the `Compliance` tool -![compliance_auto_created_ticket_final](https://codium.ai/images/pr_agent/compliance_auto_created_ticket_final.png){width=365} +![compliance_auto_created_ticket_final](https://codium.ai/images/pr_agent/compliance_auto_created_ticket_final.png){width=512} ### Manual Ticket Creation Users can manually trigger the ticket creation process from the PR interface. From aaba9b6b3cabd05dcb0b167eb56a2e7d443ae7bb Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Sun, 20 Jul 2025 20:45:30 +0300 Subject: [PATCH 35/42] add to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 42b54828..9fa328f0 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ PR-Agent and Qodo Merge offer comprehensive pull request functionalities integra | | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | | | | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | | | | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | | +| | [PR to Ticket](https://qodo-merge-docs.qodo.ai/tools/pr_to_ticket/) 💎 | ✅ | ✅ | ✅ | | | | | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | | | | | | | | | | [USAGE](https://qodo-merge-docs.qodo.ai/usage-guide/) | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | ✅ | From 1663eaad4a356ff38fb100884f1872997a26273c Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Sun, 20 Jul 2025 20:50:09 +0300 Subject: [PATCH 36/42] fix: reorder PR to Ticket entry in mkdocs.yml --- docs/mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index ff33d020..65b58595 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -35,12 +35,12 @@ nav: - 💎 Analyze: 'tools/analyze.md' - 💎 CI Feedback: 'tools/ci_feedback.md' - 💎 Compliance: 'tools/compliance.md' - - 💎 PR to Ticket: 'tools/pr_to_ticket.md' - 💎 Custom Prompt: 'tools/custom_prompt.md' - 💎 Generate Labels: 'tools/custom_labels.md' - 💎 Generate Tests: 'tools/test.md' - 💎 Implement: 'tools/implement.md' - 💎 Improve Components: 'tools/improve_component.md' + - 💎 PR to Ticket: 'tools/pr_to_ticket.md' - 💎 Scan Repo Discussions: 'tools/scan_repo_discussions.md' - 💎 Similar Code: 'tools/similar_code.md' - Core Abilities: From bdee6f9f3624b9b090b2d24d19b6b3e07ce481e0 Mon Sep 17 00:00:00 2001 From: furikake6000 Date: Mon, 21 Jul 2025 10:15:57 +0000 Subject: [PATCH 37/42] fix: add error handling to bitbucket file filtering --- pr_agent/algo/file_filter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/algo/file_filter.py b/pr_agent/algo/file_filter.py index 0812759f..3177d0b1 100644 --- a/pr_agent/algo/file_filter.py +++ b/pr_agent/algo/file_filter.py @@ -57,7 +57,7 @@ def filter_ignored(files, platform = 'github'): continue files = files_o elif platform == 'bitbucket_server': - files = [f for f in files if not r.match(f['path']['toString'])] + files = [f for f in files if f.get('path', {}).get('toString') and not r.match(f['path']['toString'])] elif platform == 'gitlab': # files = [f for f in files if (f['new_path'] and not r.match(f['new_path']))] files_o = [] From 441b4b37951b490ca79af461fc3d54d53d1e779d Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Mon, 21 Jul 2025 16:20:16 +0300 Subject: [PATCH 38/42] docs: add platform support info to custom labels tool documentation --- docs/docs/tools/custom_labels.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/tools/custom_labels.md b/docs/docs/tools/custom_labels.md index f22000ce..b9a9f6e7 100644 --- a/docs/docs/tools/custom_labels.md +++ b/docs/docs/tools/custom_labels.md @@ -1,3 +1,5 @@ +`Platforms supported: GitHub, GitLab` + ## Overview The `generate_labels` tool scans the PR code changes, and given a list of labels and their descriptions, it automatically suggests labels that match the PR code changes. From 940f82b6951ec62e29a6422a746123f88da01df3 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Mon, 21 Jul 2025 17:01:49 +0300 Subject: [PATCH 39/42] docs: add platform support info to similar code tool documentation --- docs/docs/tools/similar_code.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/tools/similar_code.md b/docs/docs/tools/similar_code.md index 623232f7..684f8265 100644 --- a/docs/docs/tools/similar_code.md +++ b/docs/docs/tools/similar_code.md @@ -1,3 +1,5 @@ +`Platforms supported: GitHub` + ## Overview The similar code tool retrieves the most similar code components from inside the organization's codebase, or from open-source code. From 4384740cb672b551733eb2624898bcfeb2694502 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 22 Jul 2025 10:51:54 +0300 Subject: [PATCH 40/42] fix(docs): update default values for help text and code suggestions in documentation --- docs/docs/faq/index.md | 32 ++++++++++++++++++++++++++++++++ docs/docs/tools/improve.md | 4 ++-- docs/docs/tools/review.md | 2 +- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/docs/docs/faq/index.md b/docs/docs/faq/index.md index 034f1f05..030ee1b5 100644 --- a/docs/docs/faq/index.md +++ b/docs/docs/faq/index.md @@ -88,3 +88,35 @@ ___ Note: The effort levels (1-5) are primarily meant for _comparative_ purposes, helping teams prioritize reviewing smaller PRs first. The actual review duration may vary, as the focus is on providing consistent relative effort estimates. ___ + +??? note "Q: How to reduce the "noise" generated by Qodo Merge?" + #### Answer:3 + + The default configuration of Qodo Merge is designed to balance helpful feedback with noise reduction. It reduces noise through several approaches: + + - Auto-feedback uses three highly structured tools (`/describe`, `/review`, and `/improve`), designed to be accessible at a glance without creating large visual overload + - Suggestions are presented in a table format rather than as committable comments, which are far noisier + - The 'File Walkthrough' section is folded by default, as it tends to be verbose + - Intermediate comments are avoided when creating new PRs (like "Qodo Merge is now reviewing your PR..."), which would generate email noise + + From our experience, especially in large teams or organizations, complaints about "noise" often stem from these issues: + + - **Lack of familiarity with the tool**: When Qodo Merge is installed on a new repo without proper introduction or training, users may not understand its purpose or perceive its feedback as "criticism" rather than constructive assistance and a chance to self-reflection. We recommend reviewing [this guide](https://qodo-merge-docs.qodo.ai/tools/improve/#understanding-ai-code-suggestions) to better align expectations and understand the value of Qodo Merge's feedback. + - **Feedback from multiple bots**: When multiple bots provide feedback on the same PR, it creates confusion and noise. We recommend using Qodo Merge as the primary feedback tool to streamline the process and reduce redundancy. + + Therefore, at a global configuration level, we recommend using the default configuration, which is designed to reduce noise while providing valuable feedback. + + However, if you still find the feedback too noisy, you can adjust the configuration. Since each user and team has different needs, it's definitely possible - and even recommended - to adjust configurations for specific repos as needed. + Ways to adjust the configuration for noise reduction include for example: + + - [Score thresholds for code suggestions](https://qodo-merge-docs.qodo.ai/tools/improve/#configuration-options) + - [Utilizing the `extra_instructions` field for more tailored feedback](https://qodo-merge-docs.qodo.ai/tools/improve/#extra-instructions) + - [Controlling which tools run automatically](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app-automatic-tools-when-a-new-pr-is-opened) + + Note that some users may prefer the opposite - more thorough and detailed feedback. Qodo Merge is designed to be flexible and customizable, allowing you to tailor the feedback to your team's specific needs and preferences. + Examples of ways to increase feedback include: + + - [`Exhaustive` code suggestions](https://qodo-merge-docs.qodo.ai/tools/improve/#controlling-suggestions-depth) + - [Dual-publishing mode](https://qodo-merge-docs.qodo.ai/tools/improve/#dual-publishing-mode) + - [Interactive usage](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) +___ diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index b9ea8417..4242ad1e 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -512,7 +512,7 @@ Qodo Merge uses a dynamic strategy to generate code suggestions based on the siz #### 2. Generating suggestions -- For each chunk, Qodo Merge generates up to `pr_code_suggestions.num_code_suggestions_per_chunk` suggestions (default: 4). +- For each chunk, Qodo Merge generates up to `pr_code_suggestions.num_code_suggestions_per_chunk` suggestions (default: 3). This approach has two main benefits: @@ -568,7 +568,7 @@ Note: Chunking is primarily relevant for large PRs. For most PRs (up to 600 line - + diff --git a/docs/docs/tools/review.md b/docs/docs/tools/review.md index a741cac9..9705f260 100644 --- a/docs/docs/tools/review.md +++ b/docs/docs/tools/review.md @@ -68,7 +68,7 @@ extra_instructions = "..." - + From c7511ac0d5e5726c9f029b80f16c34778568b398 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 22 Jul 2025 12:21:36 +0300 Subject: [PATCH 41/42] fix(docs): update default values for help text and code suggestions in documentation --- docs/docs/faq/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/faq/index.md b/docs/docs/faq/index.md index 030ee1b5..61cf25d7 100644 --- a/docs/docs/faq/index.md +++ b/docs/docs/faq/index.md @@ -89,7 +89,7 @@ ___ ___ -??? note "Q: How to reduce the "noise" generated by Qodo Merge?" +??? note "Q: How to reduce the noise generated by Qodo Merge?" #### Answer:3 The default configuration of Qodo Merge is designed to balance helpful feedback with noise reduction. It reduces noise through several approaches: From 436b5c7a0a6cc06875ed1a10b0b3eb79ad9de46d Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 22 Jul 2025 17:07:58 +0300 Subject: [PATCH 42/42] fix(docs): refine noise-related feedback section for clarity and user guidance --- docs/docs/faq/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/faq/index.md b/docs/docs/faq/index.md index 61cf25d7..3acb1e68 100644 --- a/docs/docs/faq/index.md +++ b/docs/docs/faq/index.md @@ -99,11 +99,11 @@ ___ - The 'File Walkthrough' section is folded by default, as it tends to be verbose - Intermediate comments are avoided when creating new PRs (like "Qodo Merge is now reviewing your PR..."), which would generate email noise - From our experience, especially in large teams or organizations, complaints about "noise" often stem from these issues: + From our experience, especially in large teams or organizations, complaints about "noise" sometimes stem from the following issues: - - **Lack of familiarity with the tool**: When Qodo Merge is installed on a new repo without proper introduction or training, users may not understand its purpose or perceive its feedback as "criticism" rather than constructive assistance and a chance to self-reflection. We recommend reviewing [this guide](https://qodo-merge-docs.qodo.ai/tools/improve/#understanding-ai-code-suggestions) to better align expectations and understand the value of Qodo Merge's feedback. - **Feedback from multiple bots**: When multiple bots provide feedback on the same PR, it creates confusion and noise. We recommend using Qodo Merge as the primary feedback tool to streamline the process and reduce redundancy. - + - **Getting familiar with the tool**: Unlike many tools that provide feedback only on demand, Qodo Merge automatically analyzes and suggests improvements for every code change. While this proactive approach can feel intimidating at first, it's designed to continuously enhance code quality and catch bugs and problems when they occur. We recommend reviewing [this guide](https://qodo-merge-docs.qodo.ai/tools/improve/#understanding-ai-code-suggestions) to help align expectations and maximize the value of Qodo Merge's auto-feedback. + Therefore, at a global configuration level, we recommend using the default configuration, which is designed to reduce noise while providing valuable feedback. However, if you still find the feedback too noisy, you can adjust the configuration. Since each user and team has different needs, it's definitely possible - and even recommended - to adjust configurations for specific repos as needed.
      default_project_key (required*)The default project key to use when creating tickets. This is required for the tool to create tickets in the ticketing system. Example: `SCRUM`.default_project_keyThe default project key for your ticketing system (e.g., `SCRUM`). This is required unless `fallback_to_git_provider_issues` is set to `true`.
      default_base_url
      enable_help_textIf set to true, the tool will display a help text in the comment. Default is true.If set to true, the tool will display a help text in the comment. Default is false.
      enable_chat_text
      enable_help_textIf set to true, the tool will display a help text in the comment. Default is true.If set to true, the tool will display a help text in the comment. Default is false.
      num_max_findings