Visitar URL original
Fix continued fraction precision by ryley05 · Pull Request #15743 · TheAlgorithms/Python · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Agent/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Python environment
.venv/
.env

# Python cache
__pycache__/
*.py[cod]
.pytest_cache/
.ruff_cache/

# IDE
.vscode/
Empty file added Agent/agent/__init__.py
Empty file.
Empty file added Agent/agent/code_editor.py
Empty file.
17 changes: 17 additions & 0 deletions Agent/agent/issue_analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

def analyze_issue(issue_description: str) -> str:
"""Create a concise investigation plan for the supplied issue."""
return f"""
Analyze this software issue:

{issue_description}

Identify:
1. The suspected bug.
2. What source code needs investigation.
3. What evidence would confirm the bug.
4. What tests should verify a correction.

Do not invent source code or propose a complete replacement
before inspecting the existing implementation.
"""
186 changes: 186 additions & 0 deletions Agent/agent/orchestrator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@

import ollama

from config import MODEL_NAME, MAX_TOOL_CALLS, ISSUE_URL
from agent import tools
from agent.issue_analyzer import analyze_issue

Check failure on line 6 in Agent/agent/orchestrator.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (I001)

Agent/agent/orchestrator.py:2:1: I001 Import block is un-sorted or un-formatted help: Organize imports

TOOL_DEFINITIONS = [
{
"type": "function",
"function": {
"name": "list_files",
"description": "List files in a directory of the target repository.",
"parameters": {
"type": "object",
"properties": {
"directory": {"type": "string"}
},
"required": ["directory"]
}
}
},
{
"type": "function",
"function": {
"name": "read_file",
"description": "Read a file from the target repository, especially CONTRIBUTING.md.",

Check failure on line 27 in Agent/agent/orchestrator.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (E501)

Agent/agent/orchestrator.py:27:89: E501 Line too long (97 > 88)
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"}
},
"required": ["path"]
}
}
},
{
"type": "function",
"function": {
"name": "search_code",
"description": "Search Python files for a piece of text.",
"parameters": {
"type": "object",
"properties": {
"term": {"type": "string"}
},
"required": ["term"]
}
}
},
{
"type": "function",
"function": {
"name": "run_tests",
"description": "Run approved checks: doctest or ruff.",
"parameters": {
"type": "object",
"properties": {
"command": {
"type": "string",
"enum": ["doctest", "ruff"]
}
},
"required": ["command"]
}
}
},
{
"type": "function",
"function": {
"name": "propose_edit",
"description": "Propose a code edit. The user must approve before it is applied.",

Check failure on line 72 in Agent/agent/orchestrator.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (E501)

Agent/agent/orchestrator.py:72:89: E501 Line too long (94 > 88)
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"old_text": {"type": "string"},
"new_text": {"type": "string"}
},
"required": ["path", "old_text", "new_text"]
}
}
}
]


AVAILABLE_TOOLS = {
"list_files": tools.list_files,
"read_file": tools.read_file,
"search_code": tools.search_code,
"run_tests": tools.run_tests,
"propose_edit": tools.propose_edit,
}


def run_agent(issue_description: str) -> None:
"""Run the AI agent's tool-selection and reasoning loop."""

messages = [
{
"role": "system",

Check failure on line 102 in Agent/agent/orchestrator.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (W293)

Agent/agent/orchestrator.py:102:1: W293 Blank line contains whitespace help: Remove whitespace from blank line
"content": (
"You are an autonomous Python repository contribution agent. "
"You must investigate the issue using repository tools before "
"recommending a solution. Follow these steps in order: "
"1. Read CONTRIBUTING.md. "
"2. Read the source file relevant to the issue. "
"3. Search for the specific problematic code. "
"4. Explain the exact cause of the bug using evidence from the code. "
"5. Identify the smallest appropriate correction. "
"6. Propose the correction using propose_edit. "
"7. If the user approves, run doctest and ruff. "
"8. Summarize the changes and test results. "
"Do not skip investigation steps. "
"Do not invent a replacement algorithm when a small correction "
"to the existing implementation is sufficient. "
"Preserve existing function signatures and behavior. "
"Never claim tests passed unless you ran them. "
"You must follow CONTRIBUTING.md. "
"Do not commit, push, or create pull requests."
)
},
{
"role": "user",
"content": (
f"Repository issue: {ISSUE_URL}\n\n"
f"{analyze_issue(issue_description)}"
)
}
]

for step in range(MAX_TOOL_CALLS):
print(f"\n--- Agent reasoning step {step + 1} ---")

response = ollama.chat(
model=MODEL_NAME,
messages=messages,
tools=TOOL_DEFINITIONS,
think=True,
)

assistant_message = response.message
messages.append(assistant_message)

if not assistant_message.tool_calls:
print("\n--- Agent final response ---")
print(assistant_message.content)

print("\nThe agent has finished its investigation.")
print("Running required verification checks...")

print("\n--- Doctests ---")
print(tools.run_tests("doctest"))

print("\n--- Ruff ---")
print(tools.run_tests("ruff"))

return

for tool_call in assistant_message.tool_calls:
name = tool_call.function.name
arguments = tool_call.function.arguments

print(f"\nAgent selected tool: {name}")
print(f"Arguments: {arguments}")

function = AVAILABLE_TOOLS.get(name)

if function is None:
result = f"Error: Unknown tool {name}."
else:
try:
result = function(**arguments)
except Exception as error:

Check failure on line 175 in Agent/agent/orchestrator.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (BLE001)

Agent/agent/orchestrator.py:175:24: BLE001 Do not catch blind exception: `Exception`
result = f"Tool error: {error}"

print(f"\nTool result:\n{result}")

messages.append({
"role": "tool",
"name": name,
"content": str(result)
})

print("\nAgent stopped because it reached the tool-call limit.")
Empty file.
Empty file added Agent/agent/test_runner.py
Empty file.
139 changes: 139 additions & 0 deletions Agent/agent/tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@

import subprocess
from pathlib import Path

Check failure on line 3 in Agent/agent/tools.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F401)

Agent/agent/tools.py:3:21: F401 `pathlib.Path` imported but unused help: Remove unused import: `pathlib.Path`

from config import REPOSITORY_PATH


def read_file(path: str) -> str:
"""Read a file from the target repository."""
root = REPOSITORY_PATH.resolve()
target = (root / path).resolve()

if not target.is_relative_to(root):
return "Error: Access outside the repository is not allowed."

if not target.is_file():
return f"Error: File not found: {path}"

try:
return target.read_text(encoding="utf-8")
except Exception as error:

Check failure on line 21 in Agent/agent/tools.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (BLE001)

Agent/agent/tools.py:21:12: BLE001 Do not catch blind exception: `Exception`
return f"Error reading file: {error}"


def list_files(directory: str = ".") -> str:
root = REPOSITORY_PATH.resolve()

if directory in ("", ".", "/"):
target = root
else:
target = (root / directory).resolve()

Check failure on line 31 in Agent/agent/tools.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (SIM108)

Agent/agent/tools.py:28:5: SIM108 Use ternary operator `target = root if directory in ("", ".", "/") else (root / directory).resolve()` instead of `if`-`else`-block help: Replace `if`-`else`-block with `target = root if directory in ("", ".", "/") else (root / directory).resolve()`

if not target.is_relative_to(root):
return "Error: Access outside the repository is not allowed."

if not target.is_dir():
return f"Error: Directory not found: {directory}"

files = [
str(path.relative_to(root))
for path in target.iterdir()
if path.is_file()
]

return "\n".join(files[:100])



def search_code(term: str) -> str:
"""Search Python files for a given text."""
results = []

for path in REPOSITORY_PATH.rglob("*.py"):
if ".git" in path.parts:
continue

try:
lines = path.read_text(encoding="utf-8").splitlines()

for number, line in enumerate(lines, start=1):
if term.lower() in line.lower():
relative = path.relative_to(REPOSITORY_PATH)
results.append(f"{relative}:{number}: {line.strip()}")

except (UnicodeDecodeError, OSError):
continue

if len(results) >= 50:
break

return "\n".join(results) if results else "No matches found."


def run_tests(command: str) -> str:
allowed = {
"doctest": [
"py", "-m", "doctest", "-v",
"maths/continued_fraction.py"
],
"ruff": [
"py", "-m", "ruff", "check",
"maths/continued_fraction.py"
],
}

if command not in allowed:
return "Error: This test command is not approved."

try:
result = subprocess.run(

Check failure on line 90 in Agent/agent/tools.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (PLW1510)

Agent/agent/tools.py:90:18: PLW1510 `subprocess.run` without explicit `check` argument help: Add explicit `check=False`

Check failure on line 90 in Agent/agent/tools.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (S603)

Agent/agent/tools.py:90:18: S603 `subprocess` call: check for execution of untrusted input
allowed[command],
cwd=REPOSITORY_PATH,
capture_output=True,
text=True,
timeout=120,
)

return (
f"Exit code: {result.returncode}\n"
f"STDOUT:\n{result.stdout}\n"
f"STDERR:\n{result.stderr}"
)

except subprocess.TimeoutExpired:
return "Error: Test timed out."
except Exception as error:
return f"Error running test: {error}"


def propose_edit(path: str, old_text: str, new_text: str) -> str:
"""Propose a file modification and request human approval."""
root = REPOSITORY_PATH.resolve()
target = (root / path).resolve()

if not target.is_relative_to(root):
return "Error: Access outside the repository is not allowed."

if not target.is_file():
return "Error: File not found."

current = target.read_text(encoding="utf-8")

if old_text not in current:
return "Error: The requested original text was not found."

updated = current.replace(old_text, new_text, 1)

print("\n--- PROPOSED CODE CHANGE ---")
print(f"File: {path}")
print("\nOriginal:\n", old_text)
print("\nReplacement:\n", new_text)

approval = input("\nApply this change? (yes/no): ").strip().lower()

if approval != "yes":
return "The user declined the proposed change. No file was modified."

target.write_text(updated, encoding="utf-8")
return f"Successfully updated {path}."
18 changes: 18 additions & 0 deletions Agent/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

from pathlib import Path

# Project locations
PROJECT_DIR = Path(__file__).resolve().parent
WORKSPACE_DIR = PROJECT_DIR.parent
REPOSITORY_PATH = WORKSPACE_DIR / "Python"

# AI model
MODEL_NAME = "qwen3:8b"

# Assignment issue
ISSUE_NUMBER = 15655
ISSUE_URL = "https://github.com/TheAlgorithms/Python/issues/15655"

# Agent permissions
MAX_TOOL_CALLS = 12
REQUIRE_EDIT_APPROVAL = True
Loading
Loading