[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/aidermacs 33dabece6a 1/3: Revamp model selection
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/aidermacs 33dabece6a 1/3: Revamp model selection |
Date: |
Fri, 21 Mar 2025 18:59:39 -0400 (EDT) |
branch: elpa/aidermacs
commit 33dabece6af5d09d252e4d0a000922b53bb63077
Author: Mingde (Matthew) Zeng <matthewzmd@posteo.net>
Commit: Mingde (Matthew) Zeng <matthewzmd@posteo.net>
Revamp model selection
Signed-off-by: Mingde (Matthew) Zeng <matthewzmd@posteo.net>
---
README.md | 5 +-
aidermacs-models.el | 135 +++++++++++++++++++++++++++++++++-------------------
aidermacs.el | 28 ++++++-----
3 files changed, 103 insertions(+), 65 deletions(-)
diff --git a/README.md b/README.md
index 4ceed1247b..b4eeae2a69 100644
--- a/README.md
+++ b/README.md
@@ -147,8 +147,9 @@ When Architect mode is enabled, the
`aidermacs-default-model` setting is ignored
```emacs-lisp
(setq aidermacs-use-architect-mode t)
-(setq aidermacs-architect-model "o1-mini") ; default
-(setq aidermacs-editor-model "deepseek/deepseek-chat") ;; defaults to
aidermacs-default-model
+;; Both default to aidermacs-default-model
+(setq aidermacs-architect-model "sonnet")
+(setq aidermacs-editor-model "deepseek/deepseek-chat")
```
*Note: These configurations will be overwritten by the existence of an
`.aider.conf.yml` file (see
[details](#Overwrite-Configuration-with-Configuration-File)).*
diff --git a/aidermacs-models.el b/aidermacs-models.el
index a48157424a..62516bd8ed 100644
--- a/aidermacs-models.el
+++ b/aidermacs-models.el
@@ -42,19 +42,16 @@
"Default AI model to use for aidermacs sessions when not in Architect mode."
:type 'string)
-(defcustom aidermacs-architect-model "sonnet"
- "Default AI model to use for architectural reasoning in aidermacs sessions."
+(defcustom aidermacs-architect-model aidermacs-default-model
+ "Default reasoning AI model to use for architect mode.
+Defaults to `aidermacs-default-model' if not explicitly set."
:type 'string)
(defcustom aidermacs-editor-model aidermacs-default-model
- "Default AI model to use for code editing in aidermacs sessions.
-Defaults to `aidermacs-default-model` if not explicitly set."
+ "Default editing AI model to use for architect mode.
+Defaults to `aidermacs-default-model' if not explicitly set."
:type 'string)
-(defcustom aidermacs-use-architect-mode nil
- "If non-nil, use separate Architect/Editor mode."
- :type 'boolean)
-
(defcustom aidermacs-popular-models
'("sonnet"
"o1-mini"
@@ -69,54 +66,94 @@ Also based on aidermacs LLM benchmark:
https://aidermacs.chat/docs/leaderboards/
(defvar aidermacs--cached-models aidermacs-popular-models
"Cache of available AI models.")
-(defun aidermacs--fetch-openai-compatible-models (url)
+(defconst aidermacs--api-providers
+ '(("https://openrouter.ai/api/v1" . ((hostname . "openrouter.ai")
+ (prefix . "openrouter")
+ (env-var . "OPENROUTER_API_KEY")
+ (auth-header . (("Authorization" .
"Bearer %s")))
+ (models-path . "/models")
+ (models-key . data)))
+ ("https://api.openai.com/v1" . ((hostname . "api.openai.com")
+ (prefix . "openai")
+ (env-var . "OPENAI_API_KEY")
+ (auth-header . (("Authorization" . "Bearer
%s")))
+ (models-path . "/models")
+ (models-key . data)))
+ ("https://api.deepseek.com" . ((hostname . "api.deepseek.com")
+ (prefix . "deepseek")
+ (env-var . "DEEPSEEK_API_KEY")
+ (auth-header . (("Authorization" . "Bearer
%s")))
+ (models-path . "/models")
+ (models-key . data)))
+ ("https://api.anthropic.com/v1" . ((hostname . "api.anthropic.com")
+ (prefix . "anthropic")
+ (env-var . "ANTHROPIC_API_KEY")
+ (auth-header . (("x-api-key" . "%s")
+ ("anthropic-version" .
"2023-06-01")))
+ (models-path . "/models")
+ (models-key . data)))
+ ("https://generativelanguage.googleapis.com/v1beta" . ((hostname .
"generativelanguage.googleapis.com")
+ (prefix . "gemini")
+ (env-var .
"GEMINI_API_KEY")
+ (auth-header . nil)
+ (models-path .
"/models?key=%s")
+ (models-key .
models)
+
(model-name-transform . (lambda (name)
+
(replace-regexp-in-string "^models/" "" name)))))
+ ("https://api.x.ai/v1" . ((hostname . "api.x.ai")
+ (prefix . "xai")
+ (env-var . "XAI_API_KEY")
+ (auth-header . (("Authorization" . "Bearer %s")))
+ (models-path . "/models")
+ (models-key . data))))
+ "Configuration for different API providers.
+Each entry maps a base URL to a configuration alist with:
+- hostname: The API hostname
+- prefix: Prefix to add to model names
+- env-var: Environment variable containing the API key
+- auth-header: Headers for authentication (nil if not needed)
+- models-path: Path to fetch models, with %s for token if needed
+- models-key: JSON key containing the models list
+- model-name-transform: Optional function to transform model names")
+
+(defun aidermacs--fetch-openai-compatible-models (url token)
"Fetch available models from an OpenAI compatible API endpoint.
URL should be the base API endpoint, e.g. https://api.openai.com/v1.
+TOKEN is the API token for authentication.
Returns a list of model names with appropriate prefixes based on the
API provider."
- (let* ((url-parsed (url-generic-parse-url url))
- (hostname (url-host url-parsed))
- (prefix (pcase hostname
- ("api.openai.com" "openai")
- ("openrouter.ai" "openrouter")
- ("api.deepseek.com" "deepseek")
- ("api.anthropic.com" "anthropic")
- ("generativelanguage.googleapis.com" "gemini")
- (_ (error "Unknown API host: %s" hostname))))
- (token (pcase hostname
- ("api.openai.com" (getenv "OPENAI_API_KEY"))
- ("openrouter.ai" (getenv "OPENROUTER_API_KEY"))
- ("api.deepseek.com" (getenv "DEEPSEEK_API_KEY"))
- ("api.anthropic.com" (getenv "ANTHROPIC_API_KEY"))
- ("generativelanguage.googleapis.com" (getenv
"GEMINI_API_KEY"))
- (_ (error "Unknown API host: %s" hostname)))))
+ (let* ((provider-config (cdr (assoc url aidermacs--api-providers)))
+ (prefix (alist-get 'prefix provider-config))
+ (auth-headers (alist-get 'auth-header provider-config))
+ (models-path (alist-get 'models-path provider-config))
+ (models-key (alist-get 'models-key provider-config))
+ (transform-fn (alist-get 'model-name-transform provider-config)))
+
+ (unless provider-config
+ (error "Unknown API URL: %s" url))
+
(with-local-quit
(with-current-buffer
(let ((url-request-extra-headers
- (pcase hostname
- ("api.anthropic.com"
- `(("x-api-key" . ,token)
- ("anthropic-version" . "2023-06-01")))
- ("generativelanguage.googleapis.com"
- nil) ; No auth headers for Gemini, key is in URL
- (_
- `(("Authorization" . ,(concat "Bearer " token)))))))
+ (when auth-headers
+ (mapcar (lambda (header)
+ (cons (car header)
+ (format (cdr header) token)))
+ auth-headers))))
(url-retrieve-synchronously
- (if (string= hostname "generativelanguage.googleapis.com")
- (concat url "/models?key=" token)
- (concat url "/models"))))
+ (concat url (if (string-match-p "%s" models-path)
+ (format models-path token)
+ models-path))))
+
(goto-char url-http-end-of-headers)
(let* ((json-object-type 'alist)
(json-data (json-read))
- (models (if (string= hostname
"generativelanguage.googleapis.com")
- (alist-get 'models json-data)
- (alist-get 'data json-data))))
+ (models (alist-get models-key json-data)))
(mapcar (lambda (model)
(concat prefix "/"
(cond
- ((string= hostname
"generativelanguage.googleapis.com")
- (replace-regexp-in-string "^models/" ""
(alist-get 'name model)))
- ((stringp model) model) ; Handle case where
model is just a string
+ (transform-fn (funcall transform-fn (alist-get
'name model)))
+ ((stringp model) model)
(t (or (alist-get 'id model)
(alist-get 'name model))))))
models))))))
@@ -146,16 +183,14 @@ This fetches models from various API providers and caches
them."
(mapcar (lambda (line)
(substring line 2)) ; Remove "- " prefix
supported-models))
- (dolist (url-token-pair '(("https://api.openai.com/v1" .
"OPENAI_API_KEY")
- ("https://openrouter.ai/api/v1" .
"OPENROUTER_API_KEY")
- ("https://api.deepseek.com" .
"DEEPSEEK_API_KEY")
- ("https://api.anthropic.com/v1" .
"ANTHROPIC_API_KEY")
-
("https://generativelanguage.googleapis.com/v1beta" . "GEMINI_API_KEY")))
- (let ((url (car url-token-pair))
- (token-value (getenv (cdr url-token-pair))))
+ (dolist (provider-entry aidermacs--api-providers)
+ (let* ((url (car provider-entry))
+ (config (cdr provider-entry))
+ (env-var (alist-get 'env-var config))
+ (token-value (getenv env-var)))
(when (and token-value (not (string-empty-p token-value)))
(condition-case err
- (let* ((fetched-models
(aidermacs--fetch-openai-compatible-models url))
+ (let* ((fetched-models
(aidermacs--fetch-openai-compatible-models url token-value))
(filtered-models (seq-filter (lambda (model)
(member model
supported-models))
fetched-models)))
diff --git a/aidermacs.el b/aidermacs.el
index bdad034aa2..dedac4c280 100644
--- a/aidermacs.el
+++ b/aidermacs.el
@@ -55,6 +55,10 @@
"Buffer-local variable to track the current aidermacs mode.
Possible values: `code', `ask', `architect', `help'.")
+(defcustom aidermacs-use-architect-mode nil
+ "If non-nil, use separate Architect/Editor mode."
+ :type 'boolean)
+
(defcustom aidermacs-config-file nil
"Path to aider configuration file.
When set, Aidermacs will pass this to aider via --config flag,
@@ -84,12 +88,12 @@ When nil, disable auto-commits requiring manual git
commits."
When nil, require explicit confirmation before applying changes."
:type 'boolean)
-(defvar aidermacs--cached-version nil
- "Cached aider version to avoid repeated version checks.")
-
(defvar aidermacs--read-string-history nil
"History list for aidermacs read string inputs.")
+(defvar aidermacs--cached-version nil
+ "Cached aider version to avoid repeated version checks.")
+
(defun aidermacs-aider-version ()
"Check the installed aider version.
Returns a version string like \"0.77.0\" or nil if version can't be determined.
@@ -147,7 +151,7 @@ This is the file name without path."
:if (lambda () aidermacs-auto-commits))
("R" "Refresh Repo Map" aidermacs-refresh-repo-map)
("h" "Session History" aidermacs-show-output-history)
- ("o" "Change Main Model" aidermacs-change-model)
+ ("o" "Switch Model" aidermacs-change-model)
("?" "Aider Meta-level Help" aidermacs-help)]]
["File Actions"
["Add Files (C-u: read-only)"
@@ -234,7 +238,7 @@ If supplied, SUFFIX is appended to the buffer name within
the earmuffs."
(t root))))
(format "*aidermacs:%s%s*"
(file-truename display-root)
- (or suffix "")))))
+ (or suffix "")))))
;;;###autoload
(defun aidermacs-run ()
@@ -270,8 +274,6 @@ This function sets up the appropriate arguments and
launches the process."
'("--config" "-c"))))
;; Check aider version for auto-accept-architect support
(aider-version (aidermacs-aider-version))
- (supports-auto-accept-architect (and aider-version
- (version<= "0.77.0"
aider-version)))
(backend-args
(if has-config-arg
;; Only need to add aidermacs-config-file manually
@@ -290,7 +292,7 @@ This function sets up the appropriate arguments and
launches the process."
;; 1. User has disabled auto-accept
(aidermacs-auto-accept-architect is nil)
;; 2. Aider version supports this flag (>= 0.77.0)
(when (and (not aidermacs-auto-accept-architect)
- supports-auto-accept-architect)
+ (version<= "0.77.0" aider-version))
'("--no-auto-accept-architect"))
(when aidermacs-subtree-only
'("--subtree-only")))))
@@ -876,11 +878,11 @@ Otherwise implement TODOs for the entire current file."
(let* ((current-line (string-trim (thing-at-point 'line t)))
(is-comment (aidermacs--is-comment-line current-line)))
(when-let* ((command (aidermacs--form-prompt
- "/architect"
- (concat "Please implement the TODO items."
- (and is-comment
- (format " on this comment: `%s`."
current-line))
- " Keep existing code structure"))))
+ "/architect"
+ (concat "Please implement the TODO items."
+ (and is-comment
+ (format " on this comment: `%s`."
current-line))
+ " Keep existing code structure"))))
(aidermacs--ensure-current-file-tracked)
(aidermacs--send-command command)))))