diff --git a/aider/args.py b/aider/args.py index 48ba2db59..47efeb7ed 100644 --- a/aider/args.py +++ b/aider/args.py @@ -158,18 +158,23 @@ def get_parser(default_config_files, git_root): env_var="OPENAI_ORGANIZATION_ID", help="Specify the OpenAI organization ID", ) + group.add_argument( + "--model-settings-file", + metavar="MODEL_SETTINGS_FILE", + default=None, + help="Specify a file with aider model settings for unknown models", + group.add_argument( + "--model-metadata-file", + metavar="MODEL_METADATA_FILE", + default=None, + help="Specify a file with context window and costs for unknown models", + ) group.add_argument( "--verify-ssl", action=argparse.BooleanOptionalAction, default=True, help="Verify the SSL cert when connecting to models (default: True)", - ) - group.add_argument( - "--model-metadata-file", - metavar="MODEL_FILE", - default=None, - help="Specify a file with context window and costs for unknown models", - ) + ) group.add_argument( "--edit-format", metavar="EDIT_FORMAT", diff --git a/aider/main.py b/aider/main.py index f9b4b5217..3acaf5afc 100644 --- a/aider/main.py +++ b/aider/main.py @@ -212,7 +212,48 @@ def parse_lint_cmds(lint_cmds, io): return return res +def generate_search_path_list(default_fname, git_root, command_line_file): + files = [] + default_file = Path(default_fname) + files.append(Path.home() / default_file) # homedir + if git_root: + files.append(Path(git_root) / default_file) # git root + if command_line_file: + files.append(command_line_file) + files.append(default_file.resolve()) + files = list(map(str, files)) + files = list(dict.fromkeys(files)) + + return files + +def register_models(git_root, model_settings_fname, io): + model_settings_files = generate_search_path_list(".aider.models.yml", git_root, model_settings_fname) + + try: + files_loaded = models.register_models(model_settings_files) + if len(files_loaded) > 0: + io.tool_output(f"Loaded {len(files_loaded)} model settings file(s)") + for file_loaded in files_loaded: + io.tool_output(f" - {file_loaded}") + except Exception as e: + io.tool_error(f"Error loading aider model settings: {e}") + return 1 + + return None +def register_litellm_models(git_root, model_metadata_fname, io): + model_metatdata_files = generate_search_path_list(".aider.litellm.models.json", git_root, model_metadata_fname) + + try: + model_metadata_files_loaded = models.register_litellm_models(model_metatdata_files) + if len(model_metadata_files_loaded) > 0: + io.tool_output(f"Loaded {len(model_metadata_files_loaded)} litellm model file(s)") + for model_metadata_file in model_metadata_files_loaded: + io.tool_output(f" - {model_metadata_file}") + except Exception as e: + io.tool_error(f"Error loading litellm models: {e}") + return 1 + def main(argv=None, input=None, output=None, force_git_root=None, return_coder=False): if argv is None: argv = sys.argv[1:] @@ -350,26 +391,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F if args.openai_organization_id: os.environ["OPENAI_ORGANIZATION"] = args.openai_organization_id - model_def_files = [] - model_def_fname = Path(".aider.models.json") - model_def_files.append(Path.home() / model_def_fname) # homedir - if git_root: - model_def_files.append(Path(git_root) / model_def_fname) # git root - if args.model_metadata_file: - model_def_files.append(args.model_metadata_file) - model_def_files.append(model_def_fname.resolve()) - model_def_files = list(map(str, model_def_files)) - model_def_files = list(dict.fromkeys(model_def_files)) - try: - model_metadata_files_loaded = models.register_models(model_def_files) - if len(model_metadata_files_loaded) > 0: - io.tool_output(f"Loaded {len(model_metadata_files_loaded)} model file(s)") - for model_metadata_file in model_metadata_files_loaded: - io.tool_output(f" - {model_metadata_file}") - except Exception as e: - io.tool_error(f"Error loading model info/cost: {e}") - return 1 - + register_models(git_root, args.model_settings_file, io) + register_litellm_models(git_root, args.model_metadata_file, io) + main_model = models.Model(args.model, weak_model=args.weak_model) lint_cmds = parse_lint_cmds(args.lint_cmd, io) diff --git a/aider/models.py b/aider/models.py index 003bc1e68..bed59ea7e 100644 --- a/aider/models.py +++ b/aider/models.py @@ -1,5 +1,6 @@ import difflib import json +import yaml import math import os import sys @@ -470,23 +471,47 @@ class Model: return validate_variables(["GROQ_API_KEY"]) return res - - -def register_models(model_def_fnames): - model_metadata_files_loaded = [] - for model_def_fname in model_def_fnames: - if not os.path.exists(model_def_fname): + +def register_models(model_settings_fnames): + files_loaded = [] + for model_settings_fname in model_settings_fnames: + if not os.path.exists(model_settings_fname): continue - model_metadata_files_loaded.append(model_def_fname) + try: - with open(model_def_fname, "r") as model_def_file: + with open(model_settings_fname, "r") as model_settings_file: + model_settings_list = yaml.safe_load(model_settings_file) + + for model_settings_dict in model_settings_list: + model_settings = ModelSettings(**model_settings_dict) + existing_model_settings = next((ms for ms in MODEL_SETTINGS if ms.name == model_settings.name), None) + + if existing_model_settings: + MODEL_SETTINGS.remove(existing_model_settings) + MODEL_SETTINGS.append(model_settings) + except Exception as e: + raise Exception(f"Error loading model settings from {model_settings_fname}: {e}") + files_loaded.append(model_settings_fname) + + return files_loaded + + +def register_litellm_models(model_fnames): + files_loaded = [] + for model_fname in model_fnames: + if not os.path.exists(model_fname): + continue + + try: + with open(model_fname, "r") as model_def_file: model_def = json.load(model_def_file) - except json.JSONDecodeError as e: - raise Exception(f"Error loading model definition from {model_def_fname}: {e}") + litellm.register_model(model_def) + except Exception as e: + raise Exception(f"Error loading model definition from {model_fname}: {e}") + + files_loaded.append(model_fname) - litellm.register_model(model_def) - - return model_metadata_files_loaded + return files_loaded def validate_variables(vars): diff --git a/website/docs/llms/warnings.md b/website/docs/llms/warnings.md index 213f7cb8d..c33acffd2 100644 --- a/website/docs/llms/warnings.md +++ b/website/docs/llms/warnings.md @@ -8,10 +8,45 @@ nav_order: 900 {% include model-warnings.md %} +## Adding settings for missing models +You can register model settings used by aider for unknown models. +Create a `.aider.models.yml` file in one of these locations: + +- Your home directory. +- The root if your git repo. +- The current directory where you launch aider. +- Or specify a specific file with the `--model-settings-file ` switch. + +If the files above exist, they will be loaded in that order. +Files loaded last will take priority. + +The yaml file should be a a list of dictionary objects for each model, as follows: + +``` +- name: "gpt-3.5-turbo" + edit_format: "whole" + weak_model_name: "gpt-3.5-turbo" + use_repo_map: false + send_undo_reply: false + accepts_images: false + lazy: false + reminder_as_sys_msg: true + examples_as_sys_msg: false +- name: "gpt-4-turbo-2024-04-09" + edit_format: "udiff" + weak_model_name: "gpt-3.5-turbo" + use_repo_map: true + send_undo_reply: true + accepts_images: true + lazy: true + reminder_as_sys_msg: true + examples_as_sys_msg: false +``` + ## Specifying context window size and token costs You can register context window limits and costs for models that aren't known -to aider. Create a `.aider.models.json` file in one of these locations: +to aider. Create a `.aider.litellm.models.json` file in one of these locations: - Your home directory. - The root if your git repo. diff --git a/website/docs/options.md b/website/docs/options.md index 166b2853b..204c68337 100644 --- a/website/docs/options.md +++ b/website/docs/options.md @@ -14,6 +14,14 @@ from aider.args import get_md_help cog.out(get_md_help()) ]]]--> ``` +usage: aider [-h] [--vim] [--openai-api-key] [--anthropic-api-key] + [--model] [--opus] [--sonnet] [--4] [--4o] [--4-turbo] + [--35turbo] [--models] [--openai-api-base] + [--openai-api-type] [--openai-api-version] + [--openai-api-deployment-id] [--openai-organization-id] + [--model-settings-file] [--model-metadata-file] + [--edit-format] [--weak-model] +======= usage: aider [-h] [--llm-history-file] [--openai-api-key] [--anthropic-api-key] [--model] [--opus] [--sonnet] [--4] [--4o] [--4-turbo] [--35turbo] [--models] @@ -129,6 +137,10 @@ Environment variable: `OPENAI_API_DEPLOYMENT_ID` Specify the OpenAI organization ID Environment variable: `OPENAI_ORGANIZATION_ID` +### `--model-settings-file MODEL_FILE` +Specify a file with aider model settings for unknown models +Environment variable: `AIDER_MODEL_SETTINGS_FILE` +======= ### `--verify-ssl` Verify the SSL cert when connecting to models (default: True) Default: True