summaryrefslogtreecommitdiff
path: root/modules/ai-config.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-03-06 21:20:29 -0600
committerCraig Jennings <c@cjennings.net>2026-03-06 21:20:29 -0600
commit3b120b35fff1e21114c7ff189def31b538c7a2ac (patch)
tree39b717b0f433f38d72240ed5a17b73bcad8eefb4 /modules/ai-config.el
parent3eb1a0ccaa37410e6fe0059a9cb10145efa0d615 (diff)
refactor(gptel): extract model-list and selection logic for testability
- Extract cj/gptel--build-model-list from cj/gptel-change-model - Extract cj/gptel--current-model-selection from cj/gptel-change-model - Add test-ai-config-build-model-list.el (9 tests) - Add test-ai-config-current-model-selection.el (8 tests) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'modules/ai-config.el')
-rw-r--r--modules/ai-config.el62
1 files changed, 42 insertions, 20 deletions
diff --git a/modules/ai-config.el b/modules/ai-config.el
index d8e73cf9..651acc74 100644
--- a/modules/ai-config.el
+++ b/modules/ai-config.el
@@ -118,38 +118,60 @@ Ensures gptel and backends are initialized."
((symbolp m) (symbol-name m))
(t (format "%s" m))))
-;; Backend/model switching commands (moved out of use-package so they are commandp)
+;; Backend/model switching helpers (pure logic, extracted for testability)
+
+(defun cj/gptel--build-model-list (backends model-fn)
+ "Build a flat list of all models across BACKENDS.
+BACKENDS is an alist of (NAME . BACKEND-OBJECT). MODEL-FN is called
+with each backend object and should return a list of model identifiers.
+Returns a list of entries: (DISPLAY-STRING BACKEND MODEL-STRING BACKEND-NAME)
+where DISPLAY-STRING is \"Backend: model\" for use in completing-read."
+ (mapcan
+ (lambda (pair)
+ (let* ((backend-name (car pair))
+ (backend (cdr pair))
+ (models (funcall model-fn backend)))
+ (mapcar (lambda (m)
+ (list (format "%s: %s" backend-name (cj/gptel--model-to-string m))
+ backend
+ (cj/gptel--model-to-string m)
+ backend-name))
+ models)))
+ backends))
+
+(defun cj/gptel--current-model-selection (backends current-backend current-model)
+ "Format the current backend/model as a display string.
+BACKENDS is the alist from `cj/gptel--available-backends'.
+CURRENT-BACKEND and CURRENT-MODEL are the active gptel settings.
+Returns a string like \"Anthropic - Claude: claude-opus-4-6\"."
+ (let ((backend-name (car (rassoc current-backend backends))))
+ (format "%s: %s"
+ (or backend-name "AI")
+ (cj/gptel--model-to-string current-model))))
+
+;; Backend/model switching commands
(defun cj/gptel-change-model ()
"Change the GPTel backend and select a model from that backend.
Present all available models from every backend, switching backends when
necessary. Prompt for whether to apply the selection globally or buffer-locally."
(interactive)
(let* ((backends (cj/gptel--available-backends))
- (all-models
- (mapcan
- (lambda (pair)
- (let* ((backend-name (car pair))
- (backend (cdr pair))
- (models (when (fboundp 'gptel-backend-models)
- (gptel-backend-models backend))))
- (mapcar (lambda (m)
- (list (format "%s: %s" backend-name (cj/gptel--model-to-string m))
- backend
- (cj/gptel--model-to-string m)
- backend-name))
- models)))
- backends))
- (current-backend-name (car (rassoc (bound-and-true-p gptel-backend) backends)))
- (current-selection (format "%s: %s"
- (or current-backend-name "AI")
- (cj/gptel--model-to-string (bound-and-true-p gptel-model))))
+ (all-models (cj/gptel--build-model-list
+ backends
+ (lambda (b)
+ (when (fboundp 'gptel-backend-models)
+ (gptel-backend-models b)))))
+ (current-selection (cj/gptel--current-model-selection
+ backends
+ (bound-and-true-p gptel-backend)
+ (bound-and-true-p gptel-model)))
(scope (completing-read "Set model for: " '("buffer" "global") nil t))
(selected (completing-read
(format "Select model (current: %s): " current-selection)
(mapcar #'car all-models) nil t nil nil current-selection)))
(let* ((model-info (assoc selected all-models))
(backend (nth 1 model-info))
- (model (intern (nth 2 model-info))) ;; Convert string to symbol
+ (model (intern (nth 2 model-info)))
(backend-name (nth 3 model-info)))
(if (string= scope "global")
(progn