From 5573a31edd5a485e05b6100302d04493a232beb4 Mon Sep 17 00:00:00 2001 From: Vic <125237471+vicsanity623@users.noreply.github.com> Date: Wed, 11 Mar 2026 07:56:45 -0700 Subject: [PATCH 1/5] Update autoreviewer.py --- src/pyob/autoreviewer.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pyob/autoreviewer.py b/src/pyob/autoreviewer.py index 734831e..24f9b73 100644 --- a/src/pyob/autoreviewer.py +++ b/src/pyob/autoreviewer.py @@ -213,9 +213,15 @@ def get_valid_edit( if not available_keys: if is_cloud: - logger.warning( - "☁️ Cloud environment: Gemini keys exhausted/limited. Sleeping 60s for refill..." - ) + # Check if we've been stuck for a while + logger.warning("☁️ Cloud environment: Keys exhausted. Checking if cooldowns can be cleared...") + + # FORCE RESET: If we are in the cloud and all keys are dead, + # we must clear the cooldowns to attempt a retry, + # otherwise the pipeline is effectively dead. + for key in self.key_cooldowns: + self.key_cooldowns[key] = 0.0 + time.sleep(60) attempts += 1 continue From e0812a6c886a6850f41e9c2b1b6a8ec2b657accd Mon Sep 17 00:00:00 2001 From: Vic <125237471+vicsanity623@users.noreply.github.com> Date: Wed, 11 Mar 2026 07:59:17 -0700 Subject: [PATCH 2/5] Update models.py --- src/pyob/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyob/models.py b/src/pyob/models.py index e25c071..a69f7d3 100644 --- a/src/pyob/models.py +++ b/src/pyob/models.py @@ -287,7 +287,7 @@ def get_valid_llm_response_engine( if not response_text or response_text.startswith("ERROR_CODE_"): if key and response_text and "429" in response_text: - key_cooldowns[key] = time.time() + 1200 + key_cooldowns[key] = time.time() + 60 logger.warning(f"⚠️ Key {key[-4:]} rate-limited. Rotating...") if is_cloud: logger.warning( From 9204a24d45c69a0332614bbb2d61805b3f07e927 Mon Sep 17 00:00:00 2001 From: Vic <125237471+vicsanity623@users.noreply.github.com> Date: Wed, 11 Mar 2026 08:10:44 -0700 Subject: [PATCH 3/5] Update autoreviewer.py --- src/pyob/autoreviewer.py | 150 +++++++-------------------------------- 1 file changed, 27 insertions(+), 123 deletions(-) diff --git a/src/pyob/autoreviewer.py b/src/pyob/autoreviewer.py index 24f9b73..3687254 100644 --- a/src/pyob/autoreviewer.py +++ b/src/pyob/autoreviewer.py @@ -171,30 +171,19 @@ def get_valid_edit( print("\n" + "=" * 50) print(f"💡 AI Generation Prompt Ready: [{display_name}]") print("=" * 50) - print( - f"The AI has prepared a prompt for code generation/review of: {display_name}" - ) + user_choice_pre_llm = self.get_user_approval( - "Hit ENTER to send as-is, type 'EDIT_PROMPT' to refine the full prompt, 'AUGMENT_PROMPT' to add quick instructions, or 'SKIP' to cancel.", + "Hit ENTER to send, 'EDIT_PROMPT', 'AUGMENT_PROMPT', or 'SKIP'.", timeout=220, ) if user_choice_pre_llm == "SKIP": - logger.info("AI generation skipped by user.") return source_code, "AI generation skipped by user.", "" elif user_choice_pre_llm == "EDIT_PROMPT": - logger.info("Opening full prompt in editor for manual refinement...") prompt = self._edit_prompt_with_external_editor(prompt) - if not prompt.strip(): - logger.warning("Edited prompt is empty. Skipping AI generation.") - return source_code, "Edited prompt is empty.", "" elif user_choice_pre_llm == "AUGMENT_PROMPT": - logger.info("Opening augmentation editor to add quick instructions...") augmentation_text = self._get_user_prompt_augmentation() if augmentation_text.strip(): prompt += f"\n\n### User Augmentation:\n{augmentation_text.strip()}" - logger.info("Prompt augmented with user input.") - else: - logger.info("No augmentation provided.") attempts: int = int(0) use_ollama = False @@ -213,158 +202,73 @@ def get_valid_edit( if not available_keys: if is_cloud: - # Check if we've been stuck for a while - logger.warning("☁️ Cloud environment: Keys exhausted. Checking if cooldowns can be cleared...") - - # FORCE RESET: If we are in the cloud and all keys are dead, - # we must clear the cooldowns to attempt a retry, - # otherwise the pipeline is effectively dead. - for key in self.key_cooldowns: - self.key_cooldowns[key] = 0.0 - + logger.warning("☁️ Cloud: Keys exhausted. Force resetting cooldowns and sleeping 60s...") + for k in self.key_cooldowns: + self.key_cooldowns[k] = 0.0 time.sleep(60) attempts += 1 continue else: - if not use_ollama: - logger.warning( - "🚫 Gemini rate-limited. Falling back to Local Ollama." - ) - use_ollama = True + use_ollama = True else: use_ollama = False key = available_keys[attempts % len(available_keys)] - logger.info( - f"\n[Attempting Gemini API Key {attempts % len(available_keys) + 1}/{len(available_keys)} Available]" - ) - - if use_ollama: - logger.info("\n[Attempting Local Ollama]") - response_text = self._stream_single_llm( - prompt, key=key, context=display_name - ) + response_text = self._stream_single_llm(prompt, key=key, context=display_name) if "ERROR_CODE_413" in response_text: - logger.warning( - "⚠️ GitHub Models context too large (413). Sleeping 60s..." - ) + logger.warning("⚠️ GitHub Models context too large (413). Sleeping 60s...") time.sleep(60) attempts += 1 continue if response_text.startswith("ERROR_CODE_429"): if key: - logger.warning("⚠️ Key hit a 429 rate limit. Timeout 2m.") - self.key_cooldowns[key] = time.time() + 120 + self.key_cooldowns[key] = time.time() + 300 time.sleep(60) attempts += 1 continue if response_text.startswith("ERROR_CODE_") or not response_text.strip(): - logger.warning("⚠️ API Error or Empty Response. Backing off 60s...") + logger.warning("⚠️ API Error. Backing off 60s...") time.sleep(60) attempts += 1 continue - new_code, explanation, edit_success = self.apply_xml_edits( - source_code, response_text - ) - edit_count = len(re.findall(r"", response_text, re.IGNORECASE)) - lower_exp = explanation.lower() - ai_approved_code = ( - "no fixes needed" in lower_exp - or "looks good" in lower_exp - or "no changes needed" in lower_exp - ) - - if not require_edit and ai_approved_code: - if edit_count > 0: - logger.info( - "🤖 AI stated the code looks good, but hallucinated empty blocks. Ignoring them." - ) - return source_code, explanation, response_text + new_code, explanation, edit_success = self.apply_xml_edits(source_code, response_text) + + if not edit_success and new_code != source_code: + logger.warning("⚠️ Partial edit applied. Saving partial progress to disk...") + if target_filepath and os.path.exists(target_filepath): + with open(target_filepath, "w", encoding="utf-8") as f: + f.write(new_code) + source_code = new_code + time.sleep(15) + attempts += 1 + continue + edit_count = len(re.findall(r"", response_text, re.IGNORECASE)) if edit_count > 0 and not edit_success: - logger.warning( - f"⚠️ Partial edit failure in {display_name}. Auto-regenerating..." - ) + logger.warning("⚠️ Edit failure. Backing off 30s...") time.sleep(30) attempts += 1 continue if require_edit and new_code == source_code: - logger.warning("Search block mismatch. Rotating...") + logger.warning("No edits made. Rotating...") time.sleep(30) attempts += 1 continue - if not require_edit and new_code == source_code: - if ai_approved_code: - return new_code, explanation, response_text - else: - logger.warning("⚠️ AI provided no edit and no approval. Rotating...") - time.sleep(30) - attempts += 1 - continue - if new_code != source_code: - print("\n" + "=" * 50) - print(f"💡 AI Proposed Edit Ready for: [{display_name}]") - print("=" * 50) - diff_lines = list( - difflib.unified_diff( - source_code.splitlines(keepends=True), - new_code.splitlines(keepends=True), - fromfile="Original", - tofile="Proposed", - ) - ) - for line in diff_lines[2:22]: - clean_line = line.rstrip() - if clean_line.startswith("+"): - print(f"\033[92m{clean_line}\033[0m") - elif clean_line.startswith("-"): - print(f"\033[91m{clean_line}\033[0m") - elif clean_line.startswith("@@"): - print(f"\033[94m{clean_line}\033[0m") - else: - print(clean_line) - user_choice = self.get_user_approval( - "Hit ENTER to APPLY, type 'FULL_DIFF', 'EDIT_CODE', 'EDIT_XML', 'REGENERATE', or 'SKIP'.", - timeout=220, + "Hit ENTER to APPLY, or type 'SKIP' to cancel.", timeout=220 ) - if user_choice == "SKIP": return source_code, "Edit skipped by user.", "" - elif user_choice == "REGENERATE": - attempts += 1 - continue - elif user_choice == "EDIT_XML": - response_text = self._edit_prompt_with_external_editor( - response_text - ) - new_code, explanation, _ = self.apply_xml_edits( - source_code, response_text - ) - return new_code, explanation, response_text - elif user_choice == "EDIT_CODE": - file_ext = ( - os.path.splitext(target_filepath)[1] - if target_filepath - else ".py" - ) - edited_code = self._launch_external_code_editor( - new_code, file_suffix=file_ext - ) - return ( - edited_code, - explanation + " (User refined code manually)", - response_text, - ) - return new_code, explanation, response_text + + return new_code, explanation, response_text def scan_directory(self) -> list[str]: file_list = [] From 5b50a7c518cd8b3b5840acc0f59989ce558c40dc Mon Sep 17 00:00:00 2001 From: Vic <125237471+vicsanity623@users.noreply.github.com> Date: Wed, 11 Mar 2026 08:13:56 -0700 Subject: [PATCH 4/5] Update autoreviewer.py --- src/pyob/autoreviewer.py | 150 ++++++++++++++++++++++++++++++++------- 1 file changed, 123 insertions(+), 27 deletions(-) diff --git a/src/pyob/autoreviewer.py b/src/pyob/autoreviewer.py index 3687254..24f9b73 100644 --- a/src/pyob/autoreviewer.py +++ b/src/pyob/autoreviewer.py @@ -171,19 +171,30 @@ def get_valid_edit( print("\n" + "=" * 50) print(f"💡 AI Generation Prompt Ready: [{display_name}]") print("=" * 50) - + print( + f"The AI has prepared a prompt for code generation/review of: {display_name}" + ) user_choice_pre_llm = self.get_user_approval( - "Hit ENTER to send, 'EDIT_PROMPT', 'AUGMENT_PROMPT', or 'SKIP'.", + "Hit ENTER to send as-is, type 'EDIT_PROMPT' to refine the full prompt, 'AUGMENT_PROMPT' to add quick instructions, or 'SKIP' to cancel.", timeout=220, ) if user_choice_pre_llm == "SKIP": + logger.info("AI generation skipped by user.") return source_code, "AI generation skipped by user.", "" elif user_choice_pre_llm == "EDIT_PROMPT": + logger.info("Opening full prompt in editor for manual refinement...") prompt = self._edit_prompt_with_external_editor(prompt) + if not prompt.strip(): + logger.warning("Edited prompt is empty. Skipping AI generation.") + return source_code, "Edited prompt is empty.", "" elif user_choice_pre_llm == "AUGMENT_PROMPT": + logger.info("Opening augmentation editor to add quick instructions...") augmentation_text = self._get_user_prompt_augmentation() if augmentation_text.strip(): prompt += f"\n\n### User Augmentation:\n{augmentation_text.strip()}" + logger.info("Prompt augmented with user input.") + else: + logger.info("No augmentation provided.") attempts: int = int(0) use_ollama = False @@ -202,73 +213,158 @@ def get_valid_edit( if not available_keys: if is_cloud: - logger.warning("☁️ Cloud: Keys exhausted. Force resetting cooldowns and sleeping 60s...") - for k in self.key_cooldowns: - self.key_cooldowns[k] = 0.0 + # Check if we've been stuck for a while + logger.warning("☁️ Cloud environment: Keys exhausted. Checking if cooldowns can be cleared...") + + # FORCE RESET: If we are in the cloud and all keys are dead, + # we must clear the cooldowns to attempt a retry, + # otherwise the pipeline is effectively dead. + for key in self.key_cooldowns: + self.key_cooldowns[key] = 0.0 + time.sleep(60) attempts += 1 continue else: - use_ollama = True + if not use_ollama: + logger.warning( + "🚫 Gemini rate-limited. Falling back to Local Ollama." + ) + use_ollama = True else: use_ollama = False key = available_keys[attempts % len(available_keys)] + logger.info( + f"\n[Attempting Gemini API Key {attempts % len(available_keys) + 1}/{len(available_keys)} Available]" + ) + + if use_ollama: + logger.info("\n[Attempting Local Ollama]") - response_text = self._stream_single_llm(prompt, key=key, context=display_name) + response_text = self._stream_single_llm( + prompt, key=key, context=display_name + ) if "ERROR_CODE_413" in response_text: - logger.warning("⚠️ GitHub Models context too large (413). Sleeping 60s...") + logger.warning( + "⚠️ GitHub Models context too large (413). Sleeping 60s..." + ) time.sleep(60) attempts += 1 continue if response_text.startswith("ERROR_CODE_429"): if key: - self.key_cooldowns[key] = time.time() + 300 + logger.warning("⚠️ Key hit a 429 rate limit. Timeout 2m.") + self.key_cooldowns[key] = time.time() + 120 time.sleep(60) attempts += 1 continue if response_text.startswith("ERROR_CODE_") or not response_text.strip(): - logger.warning("⚠️ API Error. Backing off 60s...") + logger.warning("⚠️ API Error or Empty Response. Backing off 60s...") time.sleep(60) attempts += 1 continue - new_code, explanation, edit_success = self.apply_xml_edits(source_code, response_text) - - if not edit_success and new_code != source_code: - logger.warning("⚠️ Partial edit applied. Saving partial progress to disk...") - if target_filepath and os.path.exists(target_filepath): - with open(target_filepath, "w", encoding="utf-8") as f: - f.write(new_code) - source_code = new_code - time.sleep(15) - attempts += 1 - continue - + new_code, explanation, edit_success = self.apply_xml_edits( + source_code, response_text + ) edit_count = len(re.findall(r"", response_text, re.IGNORECASE)) + lower_exp = explanation.lower() + ai_approved_code = ( + "no fixes needed" in lower_exp + or "looks good" in lower_exp + or "no changes needed" in lower_exp + ) + + if not require_edit and ai_approved_code: + if edit_count > 0: + logger.info( + "🤖 AI stated the code looks good, but hallucinated empty blocks. Ignoring them." + ) + return source_code, explanation, response_text + if edit_count > 0 and not edit_success: - logger.warning("⚠️ Edit failure. Backing off 30s...") + logger.warning( + f"⚠️ Partial edit failure in {display_name}. Auto-regenerating..." + ) time.sleep(30) attempts += 1 continue if require_edit and new_code == source_code: - logger.warning("No edits made. Rotating...") + logger.warning("Search block mismatch. Rotating...") time.sleep(30) attempts += 1 continue + if not require_edit and new_code == source_code: + if ai_approved_code: + return new_code, explanation, response_text + else: + logger.warning("⚠️ AI provided no edit and no approval. Rotating...") + time.sleep(30) + attempts += 1 + continue + if new_code != source_code: + print("\n" + "=" * 50) + print(f"💡 AI Proposed Edit Ready for: [{display_name}]") + print("=" * 50) + diff_lines = list( + difflib.unified_diff( + source_code.splitlines(keepends=True), + new_code.splitlines(keepends=True), + fromfile="Original", + tofile="Proposed", + ) + ) + for line in diff_lines[2:22]: + clean_line = line.rstrip() + if clean_line.startswith("+"): + print(f"\033[92m{clean_line}\033[0m") + elif clean_line.startswith("-"): + print(f"\033[91m{clean_line}\033[0m") + elif clean_line.startswith("@@"): + print(f"\033[94m{clean_line}\033[0m") + else: + print(clean_line) + user_choice = self.get_user_approval( - "Hit ENTER to APPLY, or type 'SKIP' to cancel.", timeout=220 + "Hit ENTER to APPLY, type 'FULL_DIFF', 'EDIT_CODE', 'EDIT_XML', 'REGENERATE', or 'SKIP'.", + timeout=220, ) + if user_choice == "SKIP": return source_code, "Edit skipped by user.", "" + elif user_choice == "REGENERATE": + attempts += 1 + continue + elif user_choice == "EDIT_XML": + response_text = self._edit_prompt_with_external_editor( + response_text + ) + new_code, explanation, _ = self.apply_xml_edits( + source_code, response_text + ) + return new_code, explanation, response_text + elif user_choice == "EDIT_CODE": + file_ext = ( + os.path.splitext(target_filepath)[1] + if target_filepath + else ".py" + ) + edited_code = self._launch_external_code_editor( + new_code, file_suffix=file_ext + ) + return ( + edited_code, + explanation + " (User refined code manually)", + response_text, + ) + return new_code, explanation, response_text - - return new_code, explanation, response_text def scan_directory(self) -> list[str]: file_list = [] From 1f011263bcfa907e2a7efc52c7644d4c96378a42 Mon Sep 17 00:00:00 2001 From: vicsanity623 <125237471+vicsanity623@users.noreply.github.com> Date: Wed, 11 Mar 2026 15:14:21 +0000 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=AA=84=20PyOB:=20Automated=20Lint=20&?= =?UTF-8?q?=20Format=20Fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pyob/autoreviewer.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pyob/autoreviewer.py b/src/pyob/autoreviewer.py index 24f9b73..0be6595 100644 --- a/src/pyob/autoreviewer.py +++ b/src/pyob/autoreviewer.py @@ -214,14 +214,16 @@ def get_valid_edit( if not available_keys: if is_cloud: # Check if we've been stuck for a while - logger.warning("☁️ Cloud environment: Keys exhausted. Checking if cooldowns can be cleared...") - - # FORCE RESET: If we are in the cloud and all keys are dead, - # we must clear the cooldowns to attempt a retry, + logger.warning( + "☁️ Cloud environment: Keys exhausted. Checking if cooldowns can be cleared..." + ) + + # FORCE RESET: If we are in the cloud and all keys are dead, + # we must clear the cooldowns to attempt a retry, # otherwise the pipeline is effectively dead. for key in self.key_cooldowns: self.key_cooldowns[key] = 0.0 - + time.sleep(60) attempts += 1 continue