diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 41b23c17..337efbff 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -416,18 +416,18 @@ Qodo Merge uses a dynamic strategy to generate code suggestions based on the siz #### 1. Chunking large PRs - Qodo Merge divides large PRs into 'chunks'. -- Each chunk contains up to `pr_code_suggestions.max_context_tokens` tokens (default: 14,000). +- Each chunk contains up to `pr_code_suggestions.max_context_tokens` tokens (default: 24,000). #### 2. Generating suggestions -- For each chunk, Qodo Merge generates up to `pr_code_suggestions.num_code_suggestions_per_chunk` suggestions (default: 3). +- For each chunk, Qodo Merge generates up to `pr_code_suggestions.num_code_suggestions_per_chunk` suggestions (default: 4). This approach has two main benefits: - Scalability: The number of suggestions scales with the PR size, rather than being fixed. - Quality: By processing smaller chunks, the AI can maintain higher quality suggestions, as larger contexts tend to decrease AI performance. -Note: Chunking is primarily relevant for large PRs. For most PRs (up to 500 lines of code), Qodo Merge will be able to process the entire code in a single call. +Note: Chunking is primarily relevant for large PRs. For most PRs (up to 600 lines of code), Qodo Merge will be able to process the entire code in a single call. ## Configuration options diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index 17a973dc..59ed6718 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -12,7 +12,7 @@ from pr_agent.algo.git_patch_processing import ( from pr_agent.algo.language_handler import sort_files_by_main_languages from pr_agent.algo.token_handler import TokenHandler from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo -from pr_agent.algo.utils import ModelType, clip_tokens, get_max_tokens, get_weak_model +from pr_agent.algo.utils import ModelType, clip_tokens, get_max_tokens, get_model from pr_agent.config_loader import get_settings from pr_agent.git_providers.git_provider import GitProvider from pr_agent.log import get_logger @@ -339,7 +339,9 @@ async def retry_with_fallback_models(f: Callable, model_type: ModelType = ModelT def _get_all_models(model_type: ModelType = ModelType.REGULAR) -> List[str]: if model_type == ModelType.WEAK: - model = get_weak_model() + model = get_model('model_weak') + elif model_type == ModelType.REASONING: + model = get_model('model_reasoning') else: model = get_settings().config.model fallback_models = get_settings().config.fallback_models diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index 398df4c3..4a341386 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -30,12 +30,13 @@ from pr_agent.config_loader import get_settings, global_settings from pr_agent.log import get_logger -def get_weak_model() -> str: - if get_settings().get("config.model_weak"): +def get_model(model_type: str = "model_weak") -> str: + if model_type == "model_weak" and get_settings().get("config.model_weak"): return get_settings().config.model_weak + elif model_type == "model_reasoning" and get_settings().get("config.model_reasoning"): + return get_settings().config.model_reasoning return get_settings().config.model - class Range(BaseModel): line_start: int # should be 0-indexed line_end: int @@ -45,6 +46,7 @@ class Range(BaseModel): class ModelType(str, Enum): REGULAR = "regular" WEAK = "weak" + REASONING = "reasoning" class PRReviewHeader(str, Enum): REGULAR = "## PR Reviewer Guide" diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 09548011..79528122 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -6,8 +6,9 @@ [config] # models -model="o4-mini" -fallback_models=["gpt-4.1"] +model_reasoning="o4-mini" +model="gpt-4.1" +fallback_models=["o4-mini"] #model_weak="gpt-4o" # optional, a weaker model to use for some easier tasks # CLI git_provider="github" @@ -123,7 +124,7 @@ use_conversation_history=true [pr_code_suggestions] # /improve # -max_context_tokens=16000 +max_context_tokens=24000 # commitable_code_suggestions = false dual_publishing_score_threshold=-1 # -1 to disable, [0-10] to set the threshold (>=) for publishing a code suggestion both in a table and as commitable @@ -144,7 +145,7 @@ 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=3 +num_code_suggestions_per_chunk=4 max_number_of_calls = 3 parallel_calls = true diff --git a/pr_agent/tools/pr_code_suggestions.py b/pr_agent/tools/pr_code_suggestions.py index 1d699f39..67113dc5 100644 --- a/pr_agent/tools/pr_code_suggestions.py +++ b/pr_agent/tools/pr_code_suggestions.py @@ -19,7 +19,7 @@ from pr_agent.algo.pr_processing import (add_ai_metadata_to_diff_files, retry_with_fallback_models) from pr_agent.algo.token_handler import TokenHandler from pr_agent.algo.utils import (ModelType, load_yaml, replace_code_tags, - show_relevant_configurations, get_max_tokens, clip_tokens) + show_relevant_configurations, get_max_tokens, clip_tokens, get_model) from pr_agent.config_loader import get_settings from pr_agent.git_providers import (AzureDevopsProvider, GithubProvider, GitLabProvider, get_git_provider, @@ -121,7 +121,7 @@ class PRCodeSuggestions: # if not self.is_extended: # data = await retry_with_fallback_models(self._prepare_prediction, model_type=ModelType.REGULAR) # else: - data = await retry_with_fallback_models(self._prepare_prediction_extended, model_type=ModelType.REGULAR) + data = await retry_with_fallback_models(self.prepare_prediction_main, model_type=ModelType.REGULAR) if not data: data = {"code_suggestions": []} self.data = data @@ -416,9 +416,14 @@ class PRCodeSuggestions: data = self._prepare_pr_code_suggestions(response) # self-reflect on suggestions (mandatory, since line numbers are generated now here) - model_reflection = get_settings().config.model + model_reflect_with_reasoning = get_model('model_reasoning') + if model_reflect_with_reasoning == get_settings().config.model and model != get_settings().config.model and model == \ + get_settings().config.fallback_models[0]: + # we are using a fallback model (should not happen on regular conditions) + get_logger().warning(f"Using the same model for self-reflection as the one used for suggestions") + model_reflect_with_reasoning = model response_reflect = await self.self_reflect_on_suggestions(data["code_suggestions"], - patches_diff, model=model_reflection) + patches_diff, model=model_reflect_with_reasoning) if response_reflect: await self.analyze_self_reflection_response(data, response_reflect) else: @@ -675,7 +680,7 @@ class PRCodeSuggestions: get_logger().error(f"Error removing line numbers from patches_diff_list, error: {e}") return patches_diff_list - async def _prepare_prediction_extended(self, model: str) -> dict: + async def prepare_prediction_main(self, model: str) -> dict: # get PR diff if get_settings().pr_code_suggestions.decouple_hunks: self.patches_diff_list = get_pr_multi_diffs(self.git_provider,