Merge branch 'main' into json-coders

This commit is contained in:
Paul Gauthier 2024-08-14 10:50:23 -07:00
commit 5c24a06dc8
15 changed files with 902 additions and 259 deletions

View file

@ -1,6 +1,10 @@
# Release history # Release history
### Aider v0.50.1
- Bugfix for provider API exceptions.
### Aider v0.50.0 ### Aider v0.50.0
- Infinite output for DeepSeek Coder, Mistral models in addition to Anthropic's models. - Infinite output for DeepSeek Coder, Mistral models in addition to Anthropic's models.

View file

@ -1 +1 @@
__version__ = "0.50.1-dev" __version__ = "0.50.2-dev"

View file

@ -1244,6 +1244,7 @@ class Coder:
self.io.log_llm_history("TO LLM", format_messages(messages)) self.io.log_llm_history("TO LLM", format_messages(messages))
completion = None
try: try:
hash_object, completion = send_completion( hash_object, completion = send_completion(
model.name, model.name,
@ -1263,6 +1264,8 @@ class Coder:
except KeyboardInterrupt as kbi: except KeyboardInterrupt as kbi:
self.keyboard_interrupt() self.keyboard_interrupt()
raise kbi raise kbi
except Exception as e:
self.io.tool_error(f"Error during API call: {str(e)}")
finally: finally:
self.io.log_llm_history( self.io.log_llm_history(
"LLM RESPONSE", "LLM RESPONSE",

View file

@ -125,8 +125,8 @@ Every *SEARCH/REPLACE block* must use this format:
7. The end of the replace block: >>>>>>> REPLACE 7. The end of the replace block: >>>>>>> REPLACE
8. The closing fence: {fence[1]} 8. The closing fence: {fence[1]}
Every *SEARCH* section must *EXACTLY MATCH* the existing source code, character for character, including all comments, docstrings, etc. Every *SEARCH* section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, etc.
If the file contains code or other data wrapped/escaped in json/xml/quotes or other containers, you need to propose edits to the literal contents of the file, including the container markup.
*SEARCH/REPLACE* blocks will replace *all* matching occurrences. *SEARCH/REPLACE* blocks will replace *all* matching occurrences.
Include enough lines to make the SEARCH blocks uniquely match the lines to change. Include enough lines to make the SEARCH blocks uniquely match the lines to change.

View file

@ -724,7 +724,7 @@ class Commands:
add = result.returncode != 0 add = result.returncode != 0
else: else:
response = self.io.prompt_ask( response = self.io.prompt_ask(
"Add the output to the chat?\n(y/n/instructions)", default="" "Add the output to the chat?\n(Y/n/instructions)", default=""
).strip() ).strip()
if response.lower() in ["yes", "y"]: if response.lower() in ["yes", "y"]:

View file

@ -327,6 +327,17 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
parser = get_parser(default_config_files, git_root) parser = get_parser(default_config_files, git_root)
args, unknown = parser.parse_known_args(argv) args, unknown = parser.parse_known_args(argv)
if args.verbose:
print("Config files search order, if no --config:")
for file in default_config_files:
exists = "(exists)" if Path(file).exists() else ""
print(f" - {file} {exists}")
default_config_files.reverse()
parser = get_parser(default_config_files, git_root)
args, unknown = parser.parse_known_args(argv)
# Load the .env file specified in the arguments # Load the .env file specified in the arguments
loaded_dotenvs = load_dotenv_files(git_root, args.env_file) loaded_dotenvs = load_dotenv_files(git_root, args.env_file)

View file

@ -16,6 +16,10 @@ cog.out(text)
# Release history # Release history
### Aider v0.50.1
- Bugfix for provider API exceptions.
### Aider v0.50.0 ### Aider v0.50.0
- Infinite output for DeepSeek Coder, Mistral models in addition to Anthropic's models. - Infinite output for DeepSeek Coder, Mistral models in addition to Anthropic's models.

View file

@ -577,6 +577,7 @@
pass_rate_2: 77.4 pass_rate_2: 77.4
percent_cases_well_formed: 99.2 percent_cases_well_formed: 99.2
error_outputs: 23 error_outputs: 23
released: 2024-06-20
num_malformed_responses: 4 num_malformed_responses: 4
num_with_malformed_responses: 1 num_with_malformed_responses: 1
user_asks: 2 user_asks: 2
@ -603,6 +604,7 @@
num_malformed_responses: 0 num_malformed_responses: 0
num_with_malformed_responses: 0 num_with_malformed_responses: 0
user_asks: 0 user_asks: 0
released: 2024-03-13
lazy_comments: 0 lazy_comments: 0
syntax_errors: 0 syntax_errors: 0
indentation_errors: 0 indentation_errors: 0
@ -644,6 +646,7 @@
commit_hash: d31eef3-dirty commit_hash: d31eef3-dirty
pass_rate_1: 40.6 pass_rate_1: 40.6
pass_rate_2: 55.6 pass_rate_2: 55.6
released: 2024-07-18
percent_cases_well_formed: 100.0 percent_cases_well_formed: 100.0
error_outputs: 1 error_outputs: 1
num_malformed_responses: 0 num_malformed_responses: 0
@ -668,6 +671,7 @@
pass_rate_1: 60.9 pass_rate_1: 60.9
pass_rate_2: 69.9 pass_rate_2: 69.9
percent_cases_well_formed: 97.7 percent_cases_well_formed: 97.7
released: 2024-06-28
error_outputs: 58 error_outputs: 58
num_malformed_responses: 13 num_malformed_responses: 13
num_with_malformed_responses: 3 num_with_malformed_responses: 3
@ -690,6 +694,7 @@
commit_hash: f7ce78b-dirty commit_hash: f7ce78b-dirty
pass_rate_1: 46.6 pass_rate_1: 46.6
pass_rate_2: 63.9 pass_rate_2: 63.9
released: 2024-07-23
percent_cases_well_formed: 92.5 percent_cases_well_formed: 92.5
error_outputs: 84 error_outputs: 84
num_malformed_responses: 19 num_malformed_responses: 19
@ -716,6 +721,7 @@
percent_cases_well_formed: 100.0 percent_cases_well_formed: 100.0
error_outputs: 0 error_outputs: 0
num_malformed_responses: 0 num_malformed_responses: 0
released: 2024-07-23
num_with_malformed_responses: 0 num_with_malformed_responses: 0
user_asks: 0 user_asks: 0
lazy_comments: 0 lazy_comments: 0
@ -738,6 +744,7 @@
pass_rate_2: 72.9 pass_rate_2: 72.9
percent_cases_well_formed: 97.7 percent_cases_well_formed: 97.7
error_outputs: 13 error_outputs: 13
released: 2024-07-24
num_malformed_responses: 3 num_malformed_responses: 3
num_with_malformed_responses: 3 num_with_malformed_responses: 3
user_asks: 1 user_asks: 1
@ -763,6 +770,7 @@
error_outputs: 3 error_outputs: 3
num_malformed_responses: 0 num_malformed_responses: 0
num_with_malformed_responses: 0 num_with_malformed_responses: 0
released: 2024-07-24
user_asks: 3 user_asks: 3
lazy_comments: 0 lazy_comments: 0
syntax_errors: 1 syntax_errors: 1
@ -785,6 +793,7 @@
percent_cases_well_formed: 100.0 percent_cases_well_formed: 100.0
error_outputs: 27 error_outputs: 27
num_malformed_responses: 0 num_malformed_responses: 0
released: 2024-07-23
num_with_malformed_responses: 0 num_with_malformed_responses: 0
user_asks: 23 user_asks: 23
lazy_comments: 8 lazy_comments: 8
@ -810,6 +819,7 @@
num_malformed_responses: 0 num_malformed_responses: 0
num_with_malformed_responses: 0 num_with_malformed_responses: 0
user_asks: 0 user_asks: 0
released: 2024-07-23
lazy_comments: 0 lazy_comments: 0
syntax_errors: 0 syntax_errors: 0
indentation_errors: 0 indentation_errors: 0
@ -838,9 +848,34 @@
indentation_errors: 2 indentation_errors: 2
exhausted_context_windows: 0 exhausted_context_windows: 0
test_timeouts: 5 test_timeouts: 5
released: 2024-08-06
command: aider --model openai/gpt-4o-2024-08-06 command: aider --model openai/gpt-4o-2024-08-06
date: 2024-08-06 date: 2024-08-06
versions: 0.48.1-dev versions: 0.48.1-dev
seconds_per_case: 6.5 seconds_per_case: 6.5
total_cost: 0.0000 total_cost: 0.0000
- dirname: 2024-08-14-13-07-12--chatgpt-4o-latest-diff
test_cases: 133
model: chatgpt-4o-latest
edit_format: diff
commit_hash: b1c3769
pass_rate_1: 53.4
pass_rate_2: 69.2
percent_cases_well_formed: 97.7
error_outputs: 27
num_malformed_responses: 5
num_with_malformed_responses: 3
user_asks: 7
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
test_timeouts: 0
command: aider --model openai/chatgpt-4o-latest
date: 2024-08-14
released: 2024-08-08
versions: 0.50.2-dev
seconds_per_case: 26.3
total_cost: 3.6113

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Before After
Before After

View file

@ -28,7 +28,7 @@ Using a `.aider.conf.yml` file:
dark-mode: true dark-mode: true
``` ```
By setting an environgment variable: By setting an environment variable:
``` ```
export AIDER_DARK_MODE=true export AIDER_DARK_MODE=true

View file

@ -321,6 +321,6 @@ mod_dates = [get_last_modified_date(file) for file in files]
latest_mod_date = max(mod_dates) latest_mod_date = max(mod_dates)
cog.out(f"{latest_mod_date.strftime('%B %d, %Y.')}") cog.out(f"{latest_mod_date.strftime('%B %d, %Y.')}")
]]]--> ]]]-->
August 10, 2024. August 14, 2024.
<!--[[[end]]]--> <!--[[[end]]]-->
</p> </p>

View file

@ -23,7 +23,7 @@ You can add images to the chat just like you would
add any other file: add any other file:
- Use `/add <image-filename>` from within the chat - Use `/add <image-filename>` from within the chat
- Use `/add-clipboard-image` to paste an image from your clipboard into the chat. - Use `/clipboard` to paste an image from your clipboard into the chat.
- Launch aider with image filenames on the command line: `aider <image-filename>` along with any other command line arguments you need. - Launch aider with image filenames on the command line: `aider <image-filename>` along with any other command line arguments you need.
## Web pages ## Web pages

View file

@ -28,8 +28,6 @@ from aider.coders import Coder
from aider.dump import dump # noqa: F401 from aider.dump import dump # noqa: F401
from aider.io import InputOutput from aider.io import InputOutput
load_dotenv()
BENCHMARK_DNAME = Path(os.environ.get("AIDER_BENCHMARK_DIR", "tmp.benchmarks")) BENCHMARK_DNAME = Path(os.environ.get("AIDER_BENCHMARK_DIR", "tmp.benchmarks"))
EXERCISES_DIR_DEFAULT = "exercism-python" EXERCISES_DIR_DEFAULT = "exercism-python"
@ -39,6 +37,8 @@ app = typer.Typer(add_completion=False, pretty_exceptions_enable=False)
NUM_TESTS = (89, 133) NUM_TESTS = (89, 133)
load_dotenv(override=True)
def show_stats(dirnames, graphs): def show_stats(dirnames, graphs):
raw_rows = [] raw_rows = []
@ -378,7 +378,7 @@ def summarize_results(dirname):
pass_rate = 100 * passed_tests[i] / res.completed_tests pass_rate = 100 * passed_tests[i] / res.completed_tests
percents[i] = pass_rate percents[i] = pass_rate
# console.print(f"{pass_rate:.1f}% correct after try {i+1}") # console.print(f"{pass_rate:.1f}% correct after try {i+1}")
setattr(res, f"pass_rate_{i+1}", f"{pass_rate:.1f}") setattr(res, f"pass_rate_{i + 1}", f"{pass_rate:.1f}")
print(f"- dirname: {dirname.name}") print(f"- dirname: {dirname.name}")
style = None if res.completed_tests in NUM_TESTS else "red" style = None if res.completed_tests in NUM_TESTS else "red"
@ -393,10 +393,10 @@ def summarize_results(dirname):
console.print(f" {key}: {val}", style=style) console.print(f" {key}: {val}", style=style)
for i in range(tries): for i in range(tries):
print(f" pass_rate_{i+1}: {percents[i]:.1f}") print(f" pass_rate_{i + 1}: {percents[i]:.1f}")
pct_well_formed = 1.0 - res.num_with_malformed_responses / res.completed_tests pct_well_formed = 1.0 - res.num_with_malformed_responses / res.completed_tests
print(f" percent_cases_well_formed: {pct_well_formed*100:.1f}") print(f" percent_cases_well_formed: {pct_well_formed * 100:.1f}")
show("error_outputs") show("error_outputs")
show("num_malformed_responses") show("num_malformed_responses")
@ -564,7 +564,6 @@ def run_test_real(
fnames=fnames, fnames=fnames,
use_git=False, use_git=False,
stream=False, stream=False,
pretty=False,
verbose=verbose, verbose=verbose,
) )
coder.max_apply_update_errors = max_apply_update_errors coder.max_apply_update_errors = max_apply_update_errors
@ -591,7 +590,7 @@ def run_test_real(
coder.apply_updates() coder.apply_updates()
else: else:
response = coder.run(with_message=instructions) response = coder.run(with_message=instructions, preproc=False)
dur += time.time() - start dur += time.time() - start
if not no_aider: if not no_aider:

View file

@ -3,6 +3,8 @@ import yaml
from imgcat import imgcat from imgcat import imgcat
from matplotlib import rc from matplotlib import rc
from aider.dump import dump # noqa: 401
def plot_over_time(yaml_file): def plot_over_time(yaml_file):
with open(yaml_file, "r") as file: with open(yaml_file, "r") as file:
@ -12,49 +14,97 @@ def plot_over_time(yaml_file):
pass_rates = [] pass_rates = []
models = [] models = []
print("Debug: Raw data from YAML file:")
print(data)
for entry in data: for entry in data:
if "released" in entry and "pass_rate_2" in entry: if "released" in entry and "pass_rate_2" in entry:
dates.append(entry["released"]) dates.append(entry["released"])
pass_rates.append(entry["pass_rate_2"]) pass_rates.append(entry["pass_rate_2"])
models.append(entry["model"].split("(")[0].strip()) models.append(entry["model"].split("(")[0].strip())
print("Debug: Processed data:")
print("Dates:", dates)
print("Pass rates:", pass_rates)
print("Models:", models)
if not dates or not pass_rates:
print(
"Error: No data to plot. Check if the YAML file is empty or if the data is in the"
" expected format."
)
return
plt.rcParams["hatch.linewidth"] = 0.5 plt.rcParams["hatch.linewidth"] = 0.5
plt.rcParams["hatch.color"] = "#444444" plt.rcParams["hatch.color"] = "#444444"
rc("font", **{"family": "sans-serif", "sans-serif": ["Helvetica"], "size": 10}) rc("font", **{"family": "sans-serif", "sans-serif": ["Helvetica"], "size": 10})
plt.rcParams["text.color"] = "#444444" plt.rcParams["text.color"] = "#444444"
fig, ax = plt.subplots(figsize=(10, 5)) fig, ax = plt.subplots(figsize=(12, 6)) # Increase figure size for better visibility
print("Debug: Figure created. Plotting data...")
ax.grid(axis="y", zorder=0, lw=0.2) ax.grid(axis="y", zorder=0, lw=0.2)
for spine in ax.spines.values(): for spine in ax.spines.values():
spine.set_edgecolor("#DDDDDD") spine.set_edgecolor("#DDDDDD")
spine.set_linewidth(0.5) spine.set_linewidth(0.5)
colors = [ colors = [
"red" if "gpt-4" in model else "green" if "gpt-3.5" in model else "blue" for model in models (
"purple"
if "-4o" in model and "gpt-4o-mini" not in model
else "red" if "gpt-4" in model else "green" if "gpt-3.5" in model else "lightblue"
)
for model in models
] ]
# Separate data points by color
purple_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "purple"]
red_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "red"]
green_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "green"]
# Plot lines for purple, red, and green points
if purple_points:
purple_dates, purple_rates = zip(*sorted(purple_points))
ax.plot(purple_dates, purple_rates, c="purple", alpha=0.5, linewidth=1)
if red_points:
red_dates, red_rates = zip(*sorted(red_points))
ax.plot(red_dates, red_rates, c="red", alpha=0.5, linewidth=1)
if green_points:
green_dates, green_rates = zip(*sorted(green_points))
ax.plot(green_dates, green_rates, c="green", alpha=0.5, linewidth=1)
# Plot all points
ax.scatter(dates, pass_rates, c=colors, alpha=0.5, s=120) ax.scatter(dates, pass_rates, c=colors, alpha=0.5, s=120)
for i, model in enumerate(models): for i, model in enumerate(models):
ax.annotate( ax.annotate(
model, model,
(dates[i], pass_rates[i]), (dates[i], pass_rates[i]),
fontsize=12, fontsize=8,
alpha=0.75, alpha=0.75,
xytext=(5, 5), xytext=(5, 5),
textcoords="offset points", textcoords="offset points",
) )
ax.set_xlabel("Model release date", fontsize=18, color="#555") ax.set_xlabel("Model release date", fontsize=18, color="#555")
ax.set_ylabel("Aider code editing benchmark,\npercent completed correctly", fontsize=18, color="#555") ax.set_ylabel(
"Aider code editing benchmark,\npercent completed correctly", fontsize=18, color="#555"
)
ax.set_title("LLM code editing skill by model release date", fontsize=20) ax.set_title("LLM code editing skill by model release date", fontsize=20)
ax.set_ylim(0, 30) ax.set_ylim(0, 100) # Adjust y-axis limit to accommodate higher values
plt.xticks(fontsize=14) plt.xticks(fontsize=14, rotation=45, ha="right") # Rotate x-axis labels for better readability
plt.tight_layout(pad=3.0) plt.tight_layout(pad=3.0)
print("Debug: Saving figures...")
plt.savefig("tmp_over_time.png") plt.savefig("tmp_over_time.png")
plt.savefig("tmp_over_time.svg") plt.savefig("tmp_over_time.svg")
print("Debug: Displaying figure with imgcat...")
imgcat(fig) imgcat(fig)
print("Debug: Figure generation complete.")
# Example usage # Example usage
plot_over_time("_data/edit_leaderboard.yml") plot_over_time("aider/website/_data/edit_leaderboard.yml")

View file

@ -226,9 +226,10 @@ class TestMain(TestCase):
def test_main_exit_calls_version_check(self): def test_main_exit_calls_version_check(self):
with GitTemporaryDirectory(): with GitTemporaryDirectory():
with patch("aider.main.check_version") as mock_check_version, patch( with (
"aider.main.InputOutput" patch("aider.main.check_version") as mock_check_version,
) as mock_input_output: patch("aider.main.InputOutput") as mock_input_output,
):
main(["--exit"], input=DummyInput(), output=DummyOutput()) main(["--exit"], input=DummyInput(), output=DummyOutput())
mock_check_version.assert_called_once() mock_check_version.assert_called_once()
mock_input_output.assert_called_once() mock_input_output.assert_called_once()
@ -373,6 +374,67 @@ class TestMain(TestCase):
self.assertRegex(relevant_output, r"AIDER_DARK_MODE:\s+on") self.assertRegex(relevant_output, r"AIDER_DARK_MODE:\s+on")
self.assertRegex(relevant_output, r"dark_mode:\s+True") self.assertRegex(relevant_output, r"dark_mode:\s+True")
def test_yaml_config_file_loading(self):
with GitTemporaryDirectory() as git_dir:
git_dir = Path(git_dir)
# Create fake home directory
fake_home = git_dir / "fake_home"
fake_home.mkdir()
os.environ["HOME"] = str(fake_home)
# Create subdirectory as current working directory
cwd = git_dir / "subdir"
cwd.mkdir()
os.chdir(cwd)
# Create .aider.conf.yml files in different locations
home_config = fake_home / ".aider.conf.yml"
git_config = git_dir / ".aider.conf.yml"
cwd_config = cwd / ".aider.conf.yml"
named_config = git_dir / "named.aider.conf.yml"
cwd_config.write_text("model: gpt-4-32k\nmap-tokens: 4096\n")
git_config.write_text("model: gpt-4\nmap-tokens: 2048\n")
home_config.write_text("model: gpt-3.5-turbo\nmap-tokens: 1024\n")
named_config.write_text("model: gpt-4-1106-preview\nmap-tokens: 8192\n")
with (
patch("pathlib.Path.home", return_value=fake_home),
patch("aider.coders.Coder.create") as MockCoder,
):
# Test loading from specified config file
main(
["--yes", "--exit", "--config", str(named_config)],
input=DummyInput(),
output=DummyOutput(),
)
_, kwargs = MockCoder.call_args
self.assertEqual(kwargs["main_model"].name, "gpt-4-1106-preview")
self.assertEqual(kwargs["map_tokens"], 8192)
# Test loading from current working directory
main(["--yes", "--exit"], input=DummyInput(), output=DummyOutput())
_, kwargs = MockCoder.call_args
print("kwargs:", kwargs) # Add this line for debugging
self.assertIn("main_model", kwargs, "main_model key not found in kwargs")
self.assertEqual(kwargs["main_model"].name, "gpt-4-32k")
self.assertEqual(kwargs["map_tokens"], 4096)
# Test loading from git root
cwd_config.unlink()
main(["--yes", "--exit"], input=DummyInput(), output=DummyOutput())
_, kwargs = MockCoder.call_args
self.assertEqual(kwargs["main_model"].name, "gpt-4")
self.assertEqual(kwargs["map_tokens"], 2048)
# Test loading from home directory
git_config.unlink()
main(["--yes", "--exit"], input=DummyInput(), output=DummyOutput())
_, kwargs = MockCoder.call_args
self.assertEqual(kwargs["main_model"].name, "gpt-3.5-turbo")
self.assertEqual(kwargs["map_tokens"], 1024)
def test_map_tokens_option(self): def test_map_tokens_option(self):
with GitTemporaryDirectory(): with GitTemporaryDirectory():
with patch("aider.coders.base_coder.RepoMap") as MockRepoMap: with patch("aider.coders.base_coder.RepoMap") as MockRepoMap: