emacs-elpa-diffs
[Top][All Lists]
Advanced

[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)))))
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]