Create pull request and push after every iteration #88

Merged
Jmaa merged 4 commits from issue-87-create-pull-request-and-push-after-every-iteration into main 2025-04-21 12:20:03 +00:00
2 changed files with 55 additions and 31 deletions

View File

@ -149,12 +149,15 @@ AIDER_LINT = bash_cmd(
) )
LLM_MESSAGE_FORMAT = """{issue}\nDo not wait for explicit approval before working on code changes.""" LLM_MESSAGE_FORMAT = (
"""{issue}\nDo not wait for explicit approval before working on code changes."""
)
#CODE_MODEL = 'ollama/gemma3:4b' # CODE_MODEL = 'ollama/gemma3:4b'
CODE_MODEL = 'o3' CODE_MODEL = 'o3'
EVALUATOR_MODEL = 'ollama/gemma3:27b' EVALUATOR_MODEL = 'ollama/gemma3:27b'
def create_aider_command(issue: str) -> list[str]: def create_aider_command(issue: str) -> list[str]:
l = [ l = [
'aider', 'aider',
@ -235,7 +238,6 @@ def get_diff(cwd: Path, base_branch: str, current_branch: str) -> str:
return result.stdout.strip() return result.stdout.strip()
def push_changes( def push_changes(
repository_config: RepositoryConfig, repository_config: RepositoryConfig,
cwd: Path, cwd: Path,
@ -251,7 +253,9 @@ def push_changes(
# Get commit messages for PR description # Get commit messages for PR description
commit_messages = get_commit_messages( commit_messages = get_commit_messages(
cwd, repository_config.base_branch, branch_name, cwd,
repository_config.base_branch,
branch_name,
) )
description = f'This pull request resolves #{issue_number}\n\n' description = f'This pull request resolves #{issue_number}\n\n'
@ -277,7 +281,9 @@ def push_changes(
# Extract PR number and URL if available # Extract PR number and URL if available
return IssueResolution( return IssueResolution(
True, str(pr_response.get('number')), pr_response.get('html_url'), True,
str(pr_response.get('number')),
pr_response.get('html_url'),
) )
@ -314,6 +320,7 @@ def run_cmd(cmd: list[str], cwd: Path | None = None, check=True) -> bool:
result = subprocess.run(cmd, check=check, cwd=cwd) result = subprocess.run(cmd, check=check, cwd=cwd)
return result.returncode == 0 return result.returncode == 0
def issue_solution_round(repository_path, issue_content): def issue_solution_round(repository_path, issue_content):
# Primary Aider command # Primary Aider command
aider_command = create_aider_command(issue_content) aider_command = create_aider_command(issue_content)
@ -333,6 +340,7 @@ def issue_solution_round(repository_path, issue_content):
return True return True
def run_ollama(cwd: Path, texts: list[str]) -> str: def run_ollama(cwd: Path, texts: list[str]) -> str:
cmd = ['ollama', 'run', EVALUATOR_MODEL.removeprefix('ollama/')] cmd = ['ollama', 'run', EVALUATOR_MODEL.removeprefix('ollama/')]
print(cmd) print(cmd)
@ -343,11 +351,12 @@ def run_ollama(cwd: Path, texts: list[str]) -> str:
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
text=True, text=True,
) )
stdout, stderr = process.communicate('\n'.join(texts)) stdout, stderr = process.communicate('\n'.join(texts))
print(stdout) print(stdout)
return stdout return stdout
def parse_yes_no_answer(text: str) -> bool | None: def parse_yes_no_answer(text: str) -> bool | None:
text = text.lower().strip() text = text.lower().strip()
words = text.split('\n \t.,?-') words = text.split('\n \t.,?-')
@ -358,6 +367,7 @@ def parse_yes_no_answer(text: str) -> bool | None:
return False return False
return None return None
def run_ollama_and_get_yes_or_no(cwd, initial_texts: list[str]) -> bool: def run_ollama_and_get_yes_or_no(cwd, initial_texts: list[str]) -> bool:
texts = list(initial_texts) texts = list(initial_texts)
texts.append('Think through your answer.') texts.append('Think through your answer.')
@ -370,21 +380,27 @@ def run_ollama_and_get_yes_or_no(cwd, initial_texts: list[str]) -> bool:
texts.append(response) texts.append(response)
texts.append('Please answer either "yes" or "no".') texts.append('Please answer either "yes" or "no".')
def verify_solution(repository_path: Path, issue_content: str) -> bool: def verify_solution(repository_path: Path, issue_content: str) -> bool:
summary = run_ollama( summary = run_ollama(
repository_path, repository_path,
['Concisely summarize following changeset', [
get_diff(repository_path, 'HEAD', 'main') 'Concisely summarize following changeset',
]) get_diff(repository_path, 'HEAD', 'main'),
],
)
return run_ollama_and_get_yes_or_no( return run_ollama_and_get_yes_or_no(
repository_path, repository_path,
['Does this changeset accomplish the entire task?', [
'# Change set', 'Does this changeset accomplish the entire task?',
summary, '# Change set',
'# Issue', summary,
issue_content, '# Issue',
]) issue_content,
],
)
def get_head_commit_hash(repository_path: Path) -> str: def get_head_commit_hash(repository_path: Path) -> str:
return subprocess.run( return subprocess.run(
@ -439,23 +455,27 @@ def solve_issue_in_repository(
) )
return IssueResolution(False) return IssueResolution(False)
# Push changes and create/update the pull request on every iteration
resolution = push_changes(
repository_config,
repository_path,
branch_name,
issue_number,
issue_title,
gitea_client,
)
if not resolution.success:
return resolution
# Verify whether this is a satisfactory solution # Verify whether this is a satisfactory solution
if verify_solution(repository_path, issue_content): if verify_solution(repository_path, issue_content):
break return resolution
# Push changes
return push_changes(
repository_config,
repository_path,
branch_name,
issue_number,
issue_title,
gitea_client,
)
def solve_issues_in_repository( def solve_issues_in_repository(
repository_config: RepositoryConfig, client, seen_issues_db, repository_config: RepositoryConfig,
client,
seen_issues_db,
): ):
"""Process all open issues with the 'aider' label. """Process all open issues with the 'aider' label.

View File

@ -23,7 +23,9 @@ class TestHasCommitsOnBranch:
# Verify get_commit_messages was called with correct arguments # Verify get_commit_messages was called with correct arguments
mock_get_commit_messages.assert_called_once_with( mock_get_commit_messages.assert_called_once_with(
self.cwd, self.base_branch, self.current_branch, self.cwd,
self.base_branch,
self.current_branch,
) )
@patch('aider_gitea.get_commit_messages') @patch('aider_gitea.get_commit_messages')
@ -39,7 +41,9 @@ class TestHasCommitsOnBranch:
# Verify get_commit_messages was called with correct arguments # Verify get_commit_messages was called with correct arguments
mock_get_commit_messages.assert_called_once_with( mock_get_commit_messages.assert_called_once_with(
self.cwd, self.base_branch, self.current_branch, self.cwd,
self.base_branch,
self.current_branch,
) )
@patch('aider_gitea.get_commit_messages') @patch('aider_gitea.get_commit_messages')