aider-ce 0.88.20__py3-none-any.whl → 0.88.38__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. aider/__init__.py +1 -1
  2. aider/_version.py +2 -2
  3. aider/args.py +63 -43
  4. aider/coders/agent_coder.py +331 -79
  5. aider/coders/agent_prompts.py +3 -15
  6. aider/coders/architect_coder.py +21 -5
  7. aider/coders/base_coder.py +661 -413
  8. aider/coders/base_prompts.py +6 -3
  9. aider/coders/chat_chunks.py +39 -17
  10. aider/commands.py +79 -15
  11. aider/diffs.py +10 -9
  12. aider/exceptions.py +1 -1
  13. aider/helpers/coroutines.py +8 -0
  14. aider/helpers/requests.py +45 -0
  15. aider/history.py +5 -0
  16. aider/io.py +179 -25
  17. aider/main.py +86 -35
  18. aider/models.py +16 -8
  19. aider/queries/tree-sitter-language-pack/c-tags.scm +3 -0
  20. aider/queries/tree-sitter-language-pack/clojure-tags.scm +5 -0
  21. aider/queries/tree-sitter-language-pack/commonlisp-tags.scm +5 -0
  22. aider/queries/tree-sitter-language-pack/cpp-tags.scm +3 -0
  23. aider/queries/tree-sitter-language-pack/csharp-tags.scm +6 -0
  24. aider/queries/tree-sitter-language-pack/dart-tags.scm +5 -0
  25. aider/queries/tree-sitter-language-pack/elixir-tags.scm +5 -0
  26. aider/queries/tree-sitter-language-pack/elm-tags.scm +3 -0
  27. aider/queries/tree-sitter-language-pack/go-tags.scm +7 -0
  28. aider/queries/tree-sitter-language-pack/java-tags.scm +6 -0
  29. aider/queries/tree-sitter-language-pack/javascript-tags.scm +8 -0
  30. aider/queries/tree-sitter-language-pack/lua-tags.scm +5 -0
  31. aider/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +3 -0
  32. aider/queries/tree-sitter-language-pack/python-tags.scm +10 -0
  33. aider/queries/tree-sitter-language-pack/r-tags.scm +6 -0
  34. aider/queries/tree-sitter-language-pack/ruby-tags.scm +5 -0
  35. aider/queries/tree-sitter-language-pack/rust-tags.scm +3 -0
  36. aider/queries/tree-sitter-language-pack/solidity-tags.scm +1 -1
  37. aider/queries/tree-sitter-language-pack/swift-tags.scm +4 -1
  38. aider/queries/tree-sitter-languages/c-tags.scm +3 -0
  39. aider/queries/tree-sitter-languages/c_sharp-tags.scm +6 -0
  40. aider/queries/tree-sitter-languages/cpp-tags.scm +3 -0
  41. aider/queries/tree-sitter-languages/dart-tags.scm +2 -1
  42. aider/queries/tree-sitter-languages/elixir-tags.scm +5 -0
  43. aider/queries/tree-sitter-languages/elm-tags.scm +3 -0
  44. aider/queries/tree-sitter-languages/fortran-tags.scm +3 -0
  45. aider/queries/tree-sitter-languages/go-tags.scm +6 -0
  46. aider/queries/tree-sitter-languages/haskell-tags.scm +2 -0
  47. aider/queries/tree-sitter-languages/java-tags.scm +6 -0
  48. aider/queries/tree-sitter-languages/javascript-tags.scm +8 -0
  49. aider/queries/tree-sitter-languages/julia-tags.scm +2 -2
  50. aider/queries/tree-sitter-languages/kotlin-tags.scm +3 -0
  51. aider/queries/tree-sitter-languages/ocaml_interface-tags.scm +6 -0
  52. aider/queries/tree-sitter-languages/php-tags.scm +6 -0
  53. aider/queries/tree-sitter-languages/python-tags.scm +10 -0
  54. aider/queries/tree-sitter-languages/ruby-tags.scm +5 -0
  55. aider/queries/tree-sitter-languages/rust-tags.scm +3 -0
  56. aider/queries/tree-sitter-languages/scala-tags.scm +2 -3
  57. aider/queries/tree-sitter-languages/typescript-tags.scm +3 -0
  58. aider/queries/tree-sitter-languages/zig-tags.scm +20 -3
  59. aider/repomap.py +71 -11
  60. aider/resources/model-metadata.json +27335 -635
  61. aider/resources/model-settings.yml +190 -0
  62. aider/scrape.py +2 -0
  63. aider/tools/__init__.py +2 -0
  64. aider/tools/command.py +84 -94
  65. aider/tools/command_interactive.py +95 -110
  66. aider/tools/delete_block.py +131 -159
  67. aider/tools/delete_line.py +97 -132
  68. aider/tools/delete_lines.py +120 -160
  69. aider/tools/extract_lines.py +288 -312
  70. aider/tools/finished.py +30 -43
  71. aider/tools/git_branch.py +107 -109
  72. aider/tools/git_diff.py +44 -56
  73. aider/tools/git_log.py +39 -53
  74. aider/tools/git_remote.py +37 -51
  75. aider/tools/git_show.py +33 -47
  76. aider/tools/git_status.py +30 -44
  77. aider/tools/grep.py +214 -242
  78. aider/tools/indent_lines.py +175 -201
  79. aider/tools/insert_block.py +220 -253
  80. aider/tools/list_changes.py +65 -80
  81. aider/tools/ls.py +64 -80
  82. aider/tools/make_editable.py +57 -73
  83. aider/tools/make_readonly.py +50 -66
  84. aider/tools/remove.py +64 -80
  85. aider/tools/replace_all.py +96 -109
  86. aider/tools/replace_line.py +118 -156
  87. aider/tools/replace_lines.py +160 -197
  88. aider/tools/replace_text.py +159 -160
  89. aider/tools/show_numbered_context.py +115 -141
  90. aider/tools/thinking.py +52 -0
  91. aider/tools/undo_change.py +78 -91
  92. aider/tools/update_todo_list.py +130 -138
  93. aider/tools/utils/base_tool.py +64 -0
  94. aider/tools/utils/output.py +118 -0
  95. aider/tools/view.py +38 -54
  96. aider/tools/view_files_matching.py +131 -134
  97. aider/tools/view_files_with_symbol.py +108 -120
  98. aider/urls.py +1 -1
  99. aider/versioncheck.py +4 -3
  100. aider/website/docs/config/adv-model-settings.md +237 -0
  101. aider/website/docs/config/agent-mode.md +36 -3
  102. aider/website/docs/config/model-aliases.md +2 -1
  103. aider/website/docs/faq.md +6 -11
  104. aider/website/docs/languages.md +2 -2
  105. aider/website/docs/more/infinite-output.md +27 -0
  106. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/METADATA +112 -70
  107. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/RECORD +112 -107
  108. aider_ce-0.88.38.dist-info/entry_points.txt +6 -0
  109. aider_ce-0.88.20.dist-info/entry_points.txt +0 -2
  110. /aider/tools/{tool_utils.py → utils/helpers.py} +0 -0
  111. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/WHEEL +0 -0
  112. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/licenses/LICENSE.txt +0 -0
  113. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/top_level.txt +0 -0
aider/main.py CHANGED
@@ -353,6 +353,13 @@ def register_models(git_root, model_settings_fname, io, verbose=False):
353
353
  io.tool_output(f" - {file_loaded}") # noqa: E221
354
354
  elif verbose:
355
355
  io.tool_output("No model settings files loaded")
356
+
357
+ if (
358
+ model_settings_fname
359
+ and model_settings_fname not in files_loaded
360
+ and model_settings_fname != ".aider.model.settings.yml"
361
+ ):
362
+ io.tool_warning(f"Model Settings File Not Found: {model_settings_fname}")
356
363
  except Exception as e:
357
364
  io.tool_error(f"Error loading aider model settings: {e}")
358
365
  return 1
@@ -411,6 +418,13 @@ def register_litellm_models(git_root, model_metadata_fname, io, verbose=False):
411
418
  io.tool_output("Loaded model metadata from:")
412
419
  for model_metadata_file in model_metadata_files_loaded:
413
420
  io.tool_output(f" - {model_metadata_file}") # noqa: E221
421
+
422
+ if (
423
+ model_metadata_fname
424
+ and model_metadata_fname not in model_metadata_files_loaded
425
+ and model_metadata_fname != ".aider.model.metadata.json"
426
+ ):
427
+ io.tool_warning(f"Model Metadata File Not Found: {model_metadata_fname}")
414
428
  except Exception as e:
415
429
  io.tool_error(f"Error loading model metadata models: {e}")
416
430
  return 1
@@ -478,10 +492,27 @@ def expand_glob_patterns(patterns, root="."):
478
492
 
479
493
  PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
480
494
  log_file = None
481
- file_excludelist = ["get_bottom_toolbar", "<genexpr>"]
495
+ file_excludelist = {
496
+ "get_bottom_toolbar": True,
497
+ "<genexpr>": True,
498
+ "is_active": True,
499
+ "auto_save_session": True,
500
+ "input_task": True,
501
+ "output_task": True,
502
+ }
482
503
 
483
504
 
484
505
  def custom_tracer(frame, event, arg):
506
+ try:
507
+ import os
508
+ except Exception:
509
+ return None
510
+
511
+ global log_file
512
+ if not log_file:
513
+ os.makedirs(".aider/logs/", exist_ok=True)
514
+ log_file = open(".aider/logs/debug.log", "w", buffering=1)
515
+
485
516
  # Get the absolute path of the file where the code is executing
486
517
  filename = os.path.abspath(frame.f_code.co_filename)
487
518
 
@@ -556,7 +587,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
556
587
  except AttributeError as e:
557
588
  if all(word in str(e) for word in ["bool", "object", "has", "no", "attribute", "strip"]):
558
589
  if check_config_files_for_yes(default_config_files):
559
- return 1
590
+ return await graceful_exit(None, 1)
560
591
  raise e
561
592
 
562
593
  if args.verbose:
@@ -588,7 +619,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
588
619
  # Ensure parser.prog is set for shtab, though it should be by default
589
620
  parser.prog = "aider"
590
621
  print(shtab.complete(parser, shell=args.shell_completions))
591
- sys.exit(0)
622
+ return await graceful_exit(None, 0)
592
623
 
593
624
  if git is None:
594
625
  args.git = False
@@ -677,7 +708,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
677
708
  except ValueError:
678
709
  io.tool_error(f"Invalid --set-env format: {env_setting}")
679
710
  io.tool_output("Format should be: ENV_VAR_NAME=value")
680
- return 1
711
+ return await graceful_exit(None, 1)
681
712
 
682
713
  # Process any API keys set via --api-key
683
714
  if args.api_key:
@@ -689,7 +720,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
689
720
  except ValueError:
690
721
  io.tool_error(f"Invalid --api-key format: {api_setting}")
691
722
  io.tool_output("Format should be: provider=key")
692
- return 1
723
+ return await graceful_exit(None, 1)
693
724
 
694
725
  if args.anthropic_api_key:
695
726
  os.environ["ANTHROPIC_API_KEY"] = args.anthropic_api_key
@@ -747,11 +778,11 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
747
778
  if args.gui and not return_coder:
748
779
  if not await check_streamlit_install(io):
749
780
  analytics.event("exit", reason="Streamlit not installed")
750
- return
781
+ return await graceful_exit(None)
751
782
  analytics.event("gui session")
752
783
  launch_gui(argv)
753
784
  analytics.event("exit", reason="GUI session ended")
754
- return
785
+ return await graceful_exit(None)
755
786
 
756
787
  if args.verbose:
757
788
  for fname in loaded_dotenvs:
@@ -784,7 +815,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
784
815
  "Provide either a single directory of a git repo, or a list of one or more files."
785
816
  )
786
817
  analytics.event("exit", reason="Invalid directory input")
787
- return 1
818
+ return await graceful_exit(None, 1)
788
819
 
789
820
  git_dname = None
790
821
  if len(all_files) == 1:
@@ -795,7 +826,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
795
826
  else:
796
827
  io.tool_error(f"{all_files[0]} is a directory, but --no-git selected.")
797
828
  analytics.event("exit", reason="Directory with --no-git")
798
- return 1
829
+ return await graceful_exit(None, 1)
799
830
 
800
831
  # We can't know the git repo for sure until after parsing the args.
801
832
  # If we guessed wrong, reparse because that changes things like
@@ -809,17 +840,17 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
809
840
  if args.just_check_update:
810
841
  update_available = await check_version(io, just_check=True, verbose=args.verbose)
811
842
  analytics.event("exit", reason="Just checking update")
812
- return 0 if not update_available else 1
843
+ return await graceful_exit(None, 0 if not update_available else 1)
813
844
 
814
845
  if args.install_main_branch:
815
846
  success = await install_from_main_branch(io)
816
847
  analytics.event("exit", reason="Installed main branch")
817
- return 0 if success else 1
848
+ return await graceful_exit(None, 0 if success else 1)
818
849
 
819
850
  if args.upgrade:
820
851
  success = await install_upgrade(io)
821
852
  analytics.event("exit", reason="Upgrade completed")
822
- return 0 if success else 1
853
+ return await graceful_exit(None, 0 if success else 1)
823
854
 
824
855
  if args.check_update:
825
856
  await check_version(io, verbose=args.verbose)
@@ -841,7 +872,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
841
872
  if args.list_models:
842
873
  models.print_matching_models(io, args.list_models)
843
874
  analytics.event("exit", reason="Listed models")
844
- return 0
875
+ return await graceful_exit(None)
845
876
 
846
877
  # Process any command line aliases
847
878
  if args.alias:
@@ -852,7 +883,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
852
883
  io.tool_error(f"Invalid alias format: {alias_def}")
853
884
  io.tool_output("Format should be: alias:model-name")
854
885
  analytics.event("exit", reason="Invalid alias format error")
855
- return 1
886
+ return await graceful_exit(None, 1)
856
887
  alias, model = parts
857
888
  models.MODEL_ALIASES[alias.strip()] = model.strip()
858
889
 
@@ -861,7 +892,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
861
892
  # Error message and analytics event are handled within select_default_model
862
893
  # It might have already offered OAuth if no model/keys were found.
863
894
  # If it failed here, we exit.
864
- return 1
895
+ return await graceful_exit(None, 1)
865
896
  args.model = selected_model_name # Update args with the selected model
866
897
 
867
898
  # Check if an OpenRouter model was selected/specified but the key is missing
@@ -888,7 +919,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
888
919
  "exit",
889
920
  reason="OpenRouter key missing after successful OAuth for specified model",
890
921
  )
891
- return 1
922
+ return await graceful_exit(None, 1)
892
923
  else:
893
924
  # OAuth failed or was declined by the user
894
925
  io.tool_error(
@@ -901,7 +932,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
901
932
  "exit",
902
933
  reason="OpenRouter key missing for specified model and OAuth failed/declined",
903
934
  )
904
- return 1
935
+ return await graceful_exit(None, 1)
905
936
 
906
937
  main_model = models.Model(
907
938
  args.model,
@@ -969,7 +1000,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
969
1000
  lint_cmds = parse_lint_cmds(args.lint_cmd, io)
970
1001
  if lint_cmds is None:
971
1002
  analytics.event("exit", reason="Invalid lint command format")
972
- return 1
1003
+ return await graceful_exit(None, 1)
973
1004
 
974
1005
  repo = None
975
1006
  if args.git:
@@ -995,7 +1026,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
995
1026
  if not args.skip_sanity_check_repo:
996
1027
  if not await sanity_check_repo(repo, io):
997
1028
  analytics.event("exit", reason="Repository sanity check failed")
998
- return 1
1029
+ return await graceful_exit(None, 1)
999
1030
 
1000
1031
  if repo and not args.skip_sanity_check_repo:
1001
1032
  num_files = len(repo.get_tracked_files())
@@ -1116,7 +1147,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
1116
1147
  io.tool_output()
1117
1148
  except KeyboardInterrupt:
1118
1149
  analytics.event("exit", reason="Keyboard interrupt during model warnings")
1119
- return 1
1150
+ return await graceful_exit(coder, 1)
1120
1151
 
1121
1152
  if args.git:
1122
1153
  git_root = await setup_git(git_root, io)
@@ -1129,11 +1160,12 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
1129
1160
  urls.edit_formats, "Open documentation about edit formats?", acknowledge=True
1130
1161
  )
1131
1162
  analytics.event("exit", reason="Unknown edit format")
1132
- return 1
1163
+ return await graceful_exit(None, 1)
1164
+
1133
1165
  except ValueError as err:
1134
1166
  io.tool_error(str(err))
1135
1167
  analytics.event("exit", reason="ValueError during coder creation")
1136
- return 1
1168
+ return await graceful_exit(None, 1)
1137
1169
 
1138
1170
  if return_coder:
1139
1171
  analytics.event("exit", reason="Returning coder object")
@@ -1166,7 +1198,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
1166
1198
  messages = coder.format_messages().all_messages()
1167
1199
  utils.show_messages(messages)
1168
1200
  analytics.event("exit", reason="Showed prompts")
1169
- return
1201
+ return await graceful_exit(coder)
1170
1202
 
1171
1203
  if args.lint:
1172
1204
  await coder.commands.cmd_lint(fnames=fnames)
@@ -1175,7 +1207,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
1175
1207
  if not args.test_cmd:
1176
1208
  io.tool_error("No --test-cmd provided.")
1177
1209
  analytics.event("exit", reason="No test command provided")
1178
- return 1
1210
+ return await graceful_exit(coder, 1)
1179
1211
  await coder.commands.cmd_test(args.test_cmd)
1180
1212
  if io.placeholder:
1181
1213
  await coder.run(io.placeholder)
@@ -1188,27 +1220,27 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
1188
1220
 
1189
1221
  if args.lint or args.test or args.commit:
1190
1222
  analytics.event("exit", reason="Completed lint/test/commit")
1191
- return
1223
+ return await graceful_exit(coder)
1192
1224
 
1193
1225
  if args.show_repo_map:
1194
1226
  repo_map = coder.get_repo_map()
1195
1227
  if repo_map:
1196
1228
  io.tool_output(repo_map)
1197
1229
  analytics.event("exit", reason="Showed repo map")
1198
- return
1230
+ return await graceful_exit(coder)
1199
1231
 
1200
1232
  if args.apply:
1201
1233
  content = io.read_text(args.apply)
1202
1234
  if content is None:
1203
1235
  analytics.event("exit", reason="Failed to read apply content")
1204
- return
1236
+ return await graceful_exit(coder)
1205
1237
  coder.partial_response_content = content
1206
1238
  # For testing #2879
1207
1239
  # from aider.coders.base_coder import all_fences
1208
1240
  # coder.fence = all_fences[1]
1209
1241
  await coder.apply_updates()
1210
1242
  analytics.event("exit", reason="Applied updates")
1211
- return
1243
+ return await graceful_exit(coder)
1212
1244
 
1213
1245
  if args.apply_clipboard_edits:
1214
1246
  args.edit_format = main_model.editor_edit_format
@@ -1250,7 +1282,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
1250
1282
  except (SwitchCoder, KeyboardInterrupt, SystemExit):
1251
1283
  pass
1252
1284
  analytics.event("exit", reason="Completed --message")
1253
- return
1285
+ return await graceful_exit(coder)
1254
1286
 
1255
1287
  if args.message_file:
1256
1288
  try:
@@ -1262,18 +1294,18 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
1262
1294
  except FileNotFoundError:
1263
1295
  io.tool_error(f"Message file not found: {args.message_file}")
1264
1296
  analytics.event("exit", reason="Message file not found")
1265
- return 1
1297
+ return await graceful_exit(coder, 1)
1266
1298
  except IOError as e:
1267
1299
  io.tool_error(f"Error reading message file: {e}")
1268
1300
  analytics.event("exit", reason="Message file IO error")
1269
- return 1
1301
+ return await graceful_exit(coder, 1)
1270
1302
 
1271
1303
  analytics.event("exit", reason="Completed --message-file")
1272
- return
1304
+ return await graceful_exit(coder)
1273
1305
 
1274
1306
  if args.exit:
1275
1307
  analytics.event("exit", reason="Exit flag set")
1276
- return
1308
+ return await graceful_exit(coder)
1277
1309
 
1278
1310
  analytics.event("cli session", main_model=main_model, edit_format=main_model.edit_format)
1279
1311
 
@@ -1293,7 +1325,7 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
1293
1325
  coder.ok_to_warm_cache = bool(args.cache_keepalive_pings)
1294
1326
  await coder.run()
1295
1327
  analytics.event("exit", reason="Completed main CLI coder.run")
1296
- return
1328
+ return await graceful_exit(coder)
1297
1329
  except SwitchCoder as switch:
1298
1330
  coder.ok_to_warm_cache = False
1299
1331
 
@@ -1316,7 +1348,8 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
1316
1348
  coder.suppress_announcements_for_next_prompt = True
1317
1349
  except SystemExit:
1318
1350
  analytics.event("exit", reason="/exit command")
1319
- return
1351
+ sys.settrace(None)
1352
+ return await graceful_exit(coder)
1320
1353
 
1321
1354
 
1322
1355
  def is_first_run_of_new_version(io, verbose=False):
@@ -1410,6 +1443,24 @@ def load_slow_imports(swallow=True):
1410
1443
  raise e
1411
1444
 
1412
1445
 
1446
+ async def graceful_exit(coder=None, exit_code=0):
1447
+ sys.settrace(None)
1448
+
1449
+ if coder:
1450
+ if hasattr(coder, "_autosave_future"):
1451
+ await coder._autosave_future
1452
+
1453
+ for server in coder.mcp_servers:
1454
+ try:
1455
+ await server.exit_stack.aclose()
1456
+ except Exception:
1457
+ pass
1458
+
1459
+ # Commenting since this can sometimes case hanging
1460
+ # await asyncio.sleep(0.5)
1461
+ return exit_code
1462
+
1463
+
1413
1464
  if __name__ == "__main__":
1414
1465
  status = main()
1415
1466
  sys.exit(status)
aider/models.py CHANGED
@@ -12,15 +12,15 @@ from dataclasses import dataclass, fields
12
12
  from pathlib import Path
13
13
  from typing import Optional, Union
14
14
 
15
- import json5
16
15
  import yaml
17
16
  from PIL import Image
18
17
 
19
18
  from aider import __version__
20
19
  from aider.dump import dump # noqa: F401
20
+ from aider.helpers.requests import model_request_parser
21
21
  from aider.llm import litellm
22
22
  from aider.openrouter import OpenRouterModelManager
23
- from aider.sendchat import ensure_alternating_roles, sanity_check_messages
23
+ from aider.sendchat import sanity_check_messages
24
24
  from aider.utils import check_pip_install_extra
25
25
 
26
26
  RETRY_TIMEOUT = 60
@@ -98,7 +98,8 @@ MODEL_ALIASES = {
98
98
  "quasar": "openrouter/openrouter/quasar-alpha",
99
99
  "r1": "deepseek/deepseek-reasoner",
100
100
  "gemini-2.5-pro": "gemini/gemini-2.5-pro",
101
- "gemini": "gemini/gemini-2.5-pro",
101
+ "gemini-3-pro-preview": "gemini/gemini-3-pro-preview",
102
+ "gemini": "gemini/gemini-3-pro-preview",
102
103
  "gemini-exp": "gemini/gemini-2.5-pro-exp-03-25",
103
104
  "grok3": "xai/grok-3-beta",
104
105
  "optimus": "openrouter/openrouter/optimus-alpha",
@@ -438,7 +439,7 @@ class Model(ModelSettings):
438
439
  return # <--
439
440
 
440
441
  last_segment = model.split("/")[-1]
441
- if last_segment in ("gpt-5", "gpt-5-2025-08-07"):
442
+ if last_segment in ("gpt-5", "gpt-5-2025-08-07") or "gpt-5.1" in model:
442
443
  self.use_temperature = False
443
444
  self.edit_format = "diff"
444
445
  if "reasoning_effort" not in self.accepts_settings:
@@ -909,7 +910,7 @@ class Model(ModelSettings):
909
910
  if os.environ.get("AIDER_SANITY_CHECK_TURNS"):
910
911
  sanity_check_messages(messages)
911
912
 
912
- messages = ensure_alternating_roles(messages)
913
+ messages = model_request_parser(self, messages)
913
914
 
914
915
  if self.verbose:
915
916
  for message in messages:
@@ -976,6 +977,14 @@ class Model(ModelSettings):
976
977
  dump(kwargs)
977
978
  kwargs["messages"] = messages
978
979
 
980
+ # Cache System Prompts When Possible
981
+ kwargs["cache_control_injection_points"] = [
982
+ {
983
+ "location": "message",
984
+ "role": "system",
985
+ },
986
+ ]
987
+
979
988
  # Are we using github copilot?
980
989
  if "GITHUB_COPILOT_TOKEN" in os.environ or self.name.startswith("github_copilot/"):
981
990
  if "extra_headers" not in kwargs:
@@ -1000,8 +1009,7 @@ class Model(ModelSettings):
1000
1009
  from aider.exceptions import LiteLLMExceptions
1001
1010
 
1002
1011
  litellm_ex = LiteLLMExceptions()
1003
- if "deepseek-reasoner" in self.name:
1004
- messages = ensure_alternating_roles(messages)
1012
+ messages = model_request_parser(self, messages)
1005
1013
  retry_delay = 0.125
1006
1014
 
1007
1015
  if self.verbose:
@@ -1094,7 +1102,7 @@ def register_litellm_models(model_fnames):
1094
1102
  data = Path(model_fname).read_text()
1095
1103
  if not data.strip():
1096
1104
  continue
1097
- model_def = json5.loads(data)
1105
+ model_def = json.loads(data)
1098
1106
  if not model_def:
1099
1107
  continue
1100
1108
 
@@ -7,3 +7,6 @@
7
7
  (type_definition declarator: (type_identifier) @name.definition.type) @definition.type
8
8
 
9
9
  (enum_specifier name: (type_identifier) @name.definition.type) @definition.type
10
+
11
+ (preproc_include
12
+ path: (_) @name.reference.import) @reference.import
@@ -5,3 +5,8 @@
5
5
  (#match? @ignore "^def.*"))
6
6
 
7
7
  (sym_lit name: (sym_name) @name.reference.call)
8
+
9
+ (list_lit
10
+ . (sym_lit name: (sym_name) @import_call)
11
+ . (sym_lit name: (sym_name) @name.reference.import)
12
+ (#match? @import_call "^(require|use)$")) @reference.import
@@ -78,6 +78,11 @@
78
78
  ;; first element
79
79
  (list_lit . [(sym_lit) (package_lit)] @name.reference.call) @reference.call
80
80
 
81
+ (list_lit
82
+ . (sym_lit) @import_call
83
+ . (sym_lit) @name.reference.import
84
+ (#match? @import_call "^(require|load)$")) @reference.import
85
+
81
86
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
82
87
  ;;; classes
83
88
 
@@ -13,3 +13,6 @@
13
13
  (enum_specifier name: (type_identifier) @name.definition.type) @definition.type
14
14
 
15
15
  (class_specifier name: (type_identifier) @name.definition.class) @definition.class
16
+
17
+ (preproc_include
18
+ path: (_) @name.reference.import) @reference.import
@@ -24,3 +24,9 @@
24
24
  (namespace_declaration name: (identifier) @name.definition.module) @definition.module
25
25
 
26
26
  (namespace_declaration name: (identifier) @name.definition.module) @module
27
+
28
+ (using_directive
29
+ (qualified_name) @name.reference.import) @reference.import
30
+
31
+ (using_directive
32
+ (identifier) @name.reference.import) @reference.import
@@ -90,3 +90,8 @@
90
90
  (arguments
91
91
  (argument)*))?)?) @reference.call
92
92
 
93
+ (import_or_export
94
+ (library_import
95
+ (import_specification
96
+ (configurable_uri
97
+ (uri (string_literal) @name.reference.import))))) @reference.import
@@ -52,3 +52,8 @@
52
52
 
53
53
  ; * modules
54
54
  (alias) @name.reference.module @reference.module
55
+
56
+ (call
57
+ target: (identifier) @import_call
58
+ (arguments (alias) @name.reference.import)
59
+ (#any-of? @import_call "alias" "import" "require" "use")) @reference.import
@@ -17,3 +17,6 @@
17
17
  (module_declaration
18
18
  (upper_case_qid (upper_case_identifier)) @name.definition.module
19
19
  ) @definition.module
20
+
21
+ (import_clause
22
+ (upper_case_qid) @name.reference.import) @reference.import
@@ -40,3 +40,10 @@
40
40
  (var_declaration (var_spec name: (identifier) @name.definition.variable))
41
41
 
42
42
  (const_declaration (const_spec name: (identifier) @name.definition.constant))
43
+
44
+ (import_spec
45
+ path: [
46
+ (interpreted_string_literal)
47
+ (raw_string_literal)
48
+ ] @name.reference.import) @reference.import
49
+
@@ -18,3 +18,9 @@
18
18
  type: (type_identifier) @name.reference.class) @reference.class
19
19
 
20
20
  (superclass (type_identifier) @name.reference.class) @reference.class
21
+
22
+ (import_declaration
23
+ (scoped_identifier) @name.reference.import) @reference.import
24
+
25
+ (import_declaration
26
+ (identifier) @name.reference.import) @reference.import
@@ -86,3 +86,11 @@
86
86
 
87
87
  (new_expression
88
88
  constructor: (_) @name.reference.class) @reference.class
89
+
90
+ (import_statement
91
+ source: (string (string_fragment) @name.reference.import)) @reference.import
92
+
93
+ (call_expression
94
+ function: (identifier) @import_func
95
+ arguments: (arguments (string (string_fragment) @name.reference.import))
96
+ (#eq? @import_func "require")) @reference.import
@@ -32,3 +32,8 @@
32
32
  (method_index_expression
33
33
  method: (identifier) @name.reference.method)
34
34
  ]) @reference.call
35
+
36
+ (function_call
37
+ name: (identifier) @import_func
38
+ arguments: (arguments (string (string_content) @name.reference.import))
39
+ (#eq? @import_func "require")) @reference.import
@@ -96,3 +96,6 @@
96
96
  ) @definition.function
97
97
  (#strip! @doc "^\\(\\*+\\s*|\\s*\\*+\\)$")
98
98
  )
99
+
100
+ (open_module
101
+ (module_path) @name.reference.import) @reference.import
@@ -12,3 +12,13 @@
12
12
  (attribute
13
13
  attribute: (identifier) @name.reference.call)
14
14
  ]) @reference.call
15
+
16
+ (import_statement
17
+ name: (dotted_name) @name.reference.import) @reference.import
18
+
19
+ (import_statement
20
+ name: (aliased_import
21
+ name: (dotted_name) @name.reference.import)) @reference.import
22
+
23
+ (import_from_statement
24
+ module_name: (dotted_name) @name.reference.import) @reference.import
@@ -19,3 +19,9 @@
19
19
  rhs: (identifier) @name.reference.call
20
20
  )
21
21
  ) @reference.call
22
+
23
+ (call
24
+ function: (identifier) @import_call
25
+ arguments: (arguments (identifier) @name.reference.import)
26
+ (#match? @import_call "^(library|require)$")
27
+ ) @reference.import
@@ -62,3 +62,8 @@
62
62
  (#is-not? local)
63
63
  (#not-match? @name.reference.call "^(lambda|load|require|require_relative|__FILE__|__LINE__)$")
64
64
  )
65
+
66
+ (call
67
+ method: (identifier) @import_method
68
+ arguments: (argument_list (string (string_content) @name.reference.import))
69
+ (#match? @import_method "^(require|require_relative|load)$")) @reference.import
@@ -58,3 +58,6 @@
58
58
  (impl_item
59
59
  type: (type_identifier) @name.reference.implementation
60
60
  !trait) @reference.implementation
61
+
62
+ (use_declaration
63
+ argument: (_) @name.reference.import) @reference.import
@@ -40,4 +40,4 @@
40
40
 
41
41
  ;; Imports ( note that unknown is not standardised )
42
42
  (import_directive
43
- import_name: (_) @name.reference.module ) @reference.unknown
43
+ import_name: (_) @name.reference.import ) @reference.import
@@ -48,4 +48,7 @@
48
48
  ) @definition.property
49
49
 
50
50
  (function_declaration
51
- name: (simple_identifier) @name.definition.function) @definition.function
51
+ name: (simple_identifier) @name.definition.function) @definition.function
52
+
53
+ (import_declaration
54
+ "import" @name.reference.import) @reference.import
@@ -7,3 +7,6 @@
7
7
  (type_definition declarator: (type_identifier) @name.definition.type) @definition.type
8
8
 
9
9
  (enum_specifier name: (type_identifier) @name.definition.type) @definition.type
10
+
11
+ (preproc_include
12
+ path: (_) @name.reference.import) @reference.import
@@ -44,3 +44,9 @@
44
44
  (namespace_declaration
45
45
  name: (identifier) @name.definition.module
46
46
  ) @definition.module
47
+
48
+ (using_directive
49
+ (qualified_name) @name.reference.import) @reference.import
50
+
51
+ (using_directive
52
+ (identifier) @name.reference.import) @reference.import
@@ -13,3 +13,6 @@
13
13
  (enum_specifier name: (type_identifier) @name.definition.type) @definition.type
14
14
 
15
15
  (class_specifier name: (type_identifier) @name.definition.class) @definition.class
16
+
17
+ (preproc_include
18
+ path: (_) @name.reference.import) @reference.import
@@ -88,4 +88,5 @@
88
88
  (arguments
89
89
  (argument)*))?)?) @reference.call
90
90
 
91
-
91
+ (import_or_export
92
+ (string_literal) @name.reference.import) @reference.import
@@ -52,3 +52,8 @@
52
52
 
53
53
  ; * modules
54
54
  (alias) @name.reference.module @reference.module
55
+
56
+ (call
57
+ target: (identifier) @import_call
58
+ (arguments (alias) @name.reference.import)
59
+ (#match? @import_call "^(alias|import|require|use)$")) @reference.import