Check the current code quality

This commit is contained in:
Jon Michael Aanes 2025-04-21 11:02:21 +02:00
parent 727b788d01
commit f306faab16

View File

@ -151,7 +151,8 @@ AIDER_LINT = bash_cmd(
LLM_MESSAGE_FORMAT = """/code {issue}""" LLM_MESSAGE_FORMAT = """/code {issue}"""
MODEL = 'ollama/gemma3:27b' #MODEL = 'ollama/gemma3:27b'
MODEL = 'ollama/gemma3:4b'
def create_aider_command(issue: str) -> list[str]: def create_aider_command(issue: str) -> list[str]:
l = [ l = [
@ -167,8 +168,6 @@ def create_aider_command(issue: str) -> list[str]:
AIDER_LINT, AIDER_LINT,
'--auto-test', '--auto-test',
'--no-auto-lint', '--no-auto-lint',
'--read',
'CONVENTIONS.md',
'--message', '--message',
LLM_MESSAGE_FORMAT.format(issue=issue), LLM_MESSAGE_FORMAT.format(issue=issue),
'--yes', '--yes',
@ -177,6 +176,10 @@ def create_aider_command(issue: str) -> list[str]:
for key in secrets.llm_api_keys(): for key in secrets.llm_api_keys():
l += ['--api-key', key] l += ['--api-key', key]
if False:
l.append('--read')
l.append('CONVENTIONS.md')
if True: if True:
l.append('--cache-prompts') l.append('--cache-prompts')
@ -215,6 +218,18 @@ def get_commit_messages(cwd: Path, base_branch: str, current_branch: str) -> lis
return [] return []
def get_diff(cwd: Path, base_branch: str, current_branch: str) -> str:
result = subprocess.run(
['git', 'diff', f'{base_branch}..{current_branch}', '--pretty=format:%s'],
check=True,
cwd=cwd,
capture_output=True,
text=True,
)
return result.stdout.strip()
def push_changes( def push_changes(
repository_config: RepositoryConfig, repository_config: RepositoryConfig,
cwd: Path, cwd: Path,
@ -293,9 +308,68 @@ 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):
# Primary Aider command
aider_did_not_crash = run_cmd(
create_aider_command(issue_content),
repository_path,
check=False,
)
if not aider_did_not_crash:
return aider_did_not_crash
SKIP_AIDER = False # Auto-fix standard code quality stuff after aider
run_cmd(['bash', '-c', RUFF_FORMAT_AND_AUTO_FIX], repository_path, check=False)
run_cmd(['git', 'add', '.'], repository_path)
run_cmd(['git', 'commit', '-m', 'Ruff after aider'], repository_path, check=False)
return True
def run_ollama(cwd: Path, texts: list[str]) -> str:
cmd = ['ollama', 'run', MODEL.removeprefix('ollama/')]
print(cmd)
process = subprocess.Popen(
cmd,
cwd=cwd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
stdout, stderr = process.communicate('\n'.join(texts))
return stdout
def run_ollama_and_get_yes_or_no(cwd, initial_texts: list[str]) -> bool:
texts = list(initial_texts)
texts.append('Think through your answer, and end it with "yes" or "no".')
while True:
result = run_ollama(cwd, texts).lower().removesuffix('.').strip()
print(result)
if result.endswith('yes'):
return True
elif result.endswith('no'):
return False
else:
texts.append(result)
texts.append('Please answer either yes or no.')
def verify_solution(repository_path: Path, issue_content: str) -> bool:
summary = run_ollama(
repository_path,
['Concisely summarize following changeset',
get_diff(repository_path, 'HEAD', 'main')
])
print(summary)
return run_ollama_and_get_yes_or_no(
repository_path,
['Does this changeset the task?',
'# Change set',
summary,
'# Issue',
issue_content,
])
def solve_issue_in_repository( def solve_issue_in_repository(
repository_config: RepositoryConfig, repository_config: RepositoryConfig,
@ -312,7 +386,7 @@ def solve_issue_in_repository(
run_cmd(['git', 'clone', repository_config.repo_url(), repository_path]) run_cmd(['git', 'clone', repository_config.repo_url(), repository_path])
run_cmd(['bash', '-c', AIDER_TEST], repository_path) run_cmd(['bash', '-c', AIDER_TEST], repository_path)
run_cmd(['git', 'checkout', repository_config.base_branch], repository_path) run_cmd(['git', 'checkout', repository_config.base_branch], repository_path)
run_cmd(['git', 'checkout', branch_name], repository_path) run_cmd(['git', 'checkout', '-b', branch_name], repository_path)
# Run initial ruff pass before aider # Run initial ruff pass before aider
run_cmd(['bash', '-c', RUFF_FORMAT_AND_AUTO_FIX], repository_path, check=False) run_cmd(['bash', '-c', RUFF_FORMAT_AND_AUTO_FIX], repository_path, check=False)
@ -330,26 +404,19 @@ def solve_issue_in_repository(
# Run aider # Run aider
issue_content = f'# {issue_title}\n{issue_description}' issue_content = f'# {issue_title}\n{issue_description}'
if not SKIP_AIDER:
succeeded = run_cmd(
create_aider_command(issue_content),
repository_path,
check=False,
)
else:
logger.warning('Skipping aider command (for testing)')
succeeded = True
if not succeeded:
logger.error('Aider invocation failed for issue #%s', issue_number)
return IssueResolution(False)
# Auto-fix standard code quality stuff after aider while True:
run_cmd(['bash', '-c', RUFF_FORMAT_AND_AUTO_FIX], repository_path, check=False) aider_did_not_crash = issue_solution_round(repository_path, issue_content)
run_cmd(['git', 'add', '.'], repository_path) if not aider_did_not_crash:
run_cmd(['git', 'commit', '-m', 'Ruff after aider'], repository_path, check=False) logger.error('Aider invocation failed for issue #%s', issue_number)
return IssueResolution(False)
# Verify whether this is a satisfactory solution
if verify_solution(repository_path, issue_content):
break
# Check if aider made any changes beyond the initial ruff pass # Check if aider made any changes beyond the initial ruff pass
if not has_commits_on_branch(repository_path, repository_config.base_branch, branch_name) and not SKIP_AIDER: if not has_commits_on_branch(repository_path, repository_config.base_branch, branch_name):
logger.info( logger.info(
'Aider did not make any changes beyond the initial ruff pass for issue #%s', 'Aider did not make any changes beyond the initial ruff pass for issue #%s',
issue_number, issue_number,