branch: externals/ellama
commit 4ab63cdf36b46ab2de1fad5c944bb2e2431ed08c
Author: Sergey Kostyaev <[email protected]>
Commit: Sergey Kostyaev <[email protected]>

    Support generic provider model editing
    
    Allow the model transient to read and update providers that expose generic 
chat-model and url slots, while preserving existing Ollama and OpenAI-specific 
handling. Document the broader provider support and add transient tests for 
generic provider fill and construction.
---
 README.org                     | 11 ++---
 ellama-transient.el            | 54 ++++++++++++++++++++----
 ellama.info                    | 95 +++++++++++++++++++++---------------------
 tests/test-ellama-transient.el | 44 +++++++++++++++++++
 4 files changed, 144 insertions(+), 60 deletions(-)

diff --git a/README.org b/README.org
index ed0d1501e8..47bf7fd6b5 100644
--- a/README.org
+++ b/README.org
@@ -177,11 +177,12 @@ More sophisticated configuration example:
   Ellama.
 - ~ellama-provider-select~: Select ellama provider.
 - ~ellama-select-model~: Change the current provider model interactively.  The
-  model transient supports Ollama and OpenAI-compatible providers, including 
URL
-  editing for compatible APIs.  It can also set the maximum number of output
-  tokens.  Use "Reset model fields" to clear model, temperature, 
context-length,
-  and max-token overrides and let the provider use its defaults; reset values
-  are shown as ~default~ in the transient.
+  model transient supports Ollama, OpenAI-compatible providers, and providers
+  with a ~chat-model~ slot.  URL editing is available when the provider has a
+  ~url~ slot.  It can also set the maximum number of output tokens.  Use "Reset
+  model fields" to clear model, temperature, context-length, and max-token
+  overrides and let the provider use its defaults; reset values are shown as
+  ~default~ in the transient.
 - ~ellama-code-complete~: Complete selected code or code in the current buffer
   according to a provided change using Ellama.
 - ~ellama-code-add~: Generate and insert new code based on description. This
diff --git a/ellama-transient.el b/ellama-transient.el
index 1e2301db5d..ed22b57537 100644
--- a/ellama-transient.el
+++ b/ellama-transient.el
@@ -38,7 +38,7 @@
 
 (defcustom ellama-transient-system-show-limit 45
   "Maximum length of system message to show."
-  :type 'ingeger
+  :type 'integer
   :group 'ellama)
 
 (defvaralias 'ellama-transient-ollama-model-name
@@ -57,6 +57,10 @@
                   (image prompt &optional create-session &rest args))
 (declare-function ellama-chat-with-image "ellama"
                   (image prompt &optional create-session &rest args))
+(declare-function ellama--provider-slot-offset "ellama" (provider slot))
+(declare-function ellama--provider-slot-value "ellama" (provider slot))
+(declare-function ellama--provider-with-slot-value "ellama"
+                  (provider slot value))
 
 (defun ellama-transient-system-show ()
   "Show transient system message."
@@ -255,8 +259,8 @@ strings, because they may contain API keys."
                                  (format "Port (%s)" ellama-transient-port)
                                "Port")))
     ("u" "Set URL" ellama-transient-set-url
-     :if (lambda () (ellama-transient--openai-compatible-provider-p
-                     ellama-transient-provider))
+     :if (lambda () (ellama-transient--provider-slot-present-p
+                     ellama-transient-provider 'url))
      :transient t
      :description (lambda () (if ellama-transient-url
                                  (format "URL (%s)" ellama-transient-url)
@@ -320,6 +324,11 @@ FORMAT is used for non-default VALUE."
        (fboundp 'llm-openai-p)
        (llm-openai-p provider)))
 
+(defun ellama-transient--provider-slot-present-p (provider slot)
+  "Return non-nil when PROVIDER has SLOT."
+  (and provider
+       (ellama--provider-slot-offset provider slot)))
+
 (defun ellama-transient--alist (value)
   "Return VALUE as an alist."
   (cond
@@ -369,7 +378,18 @@ FORMAT is used for non-default VALUE."
    ((ellama-transient--openai-compatible-provider-p provider)
     (llm-openai-compatible-chat-model provider))
    ((ellama-transient--openai-provider-p provider)
-    (llm-openai-chat-model provider))))
+    (llm-openai-chat-model provider))
+   ((ellama-transient--provider-slot-present-p provider 'chat-model)
+    (ellama--provider-slot-value provider 'chat-model))))
+
+(defun ellama-transient--provider-url (provider)
+  "Return API URL from PROVIDER."
+  (declare-function llm-openai-compatible-url "ext:llm-openai")
+  (cond
+   ((ellama-transient--openai-compatible-provider-p provider)
+    (llm-openai-compatible-url provider))
+   ((ellama-transient--provider-slot-present-p provider 'url)
+    (ellama--provider-slot-value provider 'url))))
 
 (defun ellama-transient--provider-models (provider)
   "Return available chat models for PROVIDER."
@@ -411,14 +431,12 @@ FORMAT is used for non-default VALUE."
 
 (defun ellama-fill-transient-model (provider)
   "Set transient model fields from PROVIDER."
-  (declare-function llm-openai-compatible-url "ext:llm-openai")
   (setq ellama-transient-provider provider)
   (when-let ((model (ellama-transient--provider-model provider)))
     (setq ellama-transient-model-name model))
   (setq ellama-transient-temperature
         (ellama-transient--standard-temperature provider))
-  (when (ellama-transient--openai-compatible-provider-p provider)
-    (setq ellama-transient-url (llm-openai-compatible-url provider)))
+  (setq ellama-transient-url (ellama-transient--provider-url provider))
   (ellama-transient--fill-ollama provider))
 
 (defalias 'ellama-fill-transient-ollama-model
@@ -496,6 +514,20 @@ FORMAT is used for non-default VALUE."
           (ellama-transient--effective-model provider))))
   provider)
 
+(defun ellama-transient--set-generic-provider-fields (provider)
+  "Set generic PROVIDER fields from transient."
+  (when (ellama-transient--provider-slot-present-p provider 'chat-model)
+    (setq provider
+          (ellama--provider-with-slot-value
+           provider 'chat-model
+           (ellama-transient--effective-model provider))))
+  (when (and ellama-transient-url
+             (ellama-transient--provider-slot-present-p provider 'url))
+    (setq provider
+          (ellama--provider-with-slot-value
+           provider 'url ellama-transient-url)))
+  provider)
+
 (defun ellama-construct-provider-from-transient (&optional base-provider)
   "Make provider from transient menu using BASE-PROVIDER."
   (declare-function make-llm-ollama "ext:llm-ollama")
@@ -514,6 +546,9 @@ FORMAT is used for non-default VALUE."
            ((not base-provider)
             (require 'llm-ollama)
             (make-llm-ollama))
+           ((or (recordp base-provider)
+                (vectorp base-provider))
+            (copy-sequence base-provider))
            (t
             (error "Provider type does not support transient model 
changes")))))
     (ellama-transient--set-standard-temperature provider)
@@ -522,7 +557,10 @@ FORMAT is used for non-default VALUE."
       (ellama-transient--set-ollama-fields provider))
      ((or (ellama-transient--openai-compatible-provider-p provider)
           (ellama-transient--openai-provider-p provider))
-      (ellama-transient--set-openai-fields provider)))
+      (ellama-transient--set-openai-fields provider))
+     (t
+      (setq provider
+            (ellama-transient--set-generic-provider-fields provider))))
     provider))
 
 (defun ellama-construct-ollama-provider-from-transient ()
diff --git a/ellama.info b/ellama.info
index f364a9248f..785387aaec 100644
--- a/ellama.info
+++ b/ellama.info
@@ -295,12 +295,13 @@ File: ellama.info,  Node: Commands,  Next: Keymap,  Prev: 
Installation,  Up: Top
      using Ellama.
    • ‘ellama-provider-select’: Select ellama provider.
    • ‘ellama-select-model’: Change the current provider model
-     interactively.  The model transient supports Ollama and
-     OpenAI-compatible providers, including URL editing for compatible
-     APIs.  It can also set the maximum number of output tokens.  Use
-     "Reset model fields" to clear model, temperature, context-length,
-     and max-token overrides and let the provider use its defaults;
-     reset values are shown as ‘default’ in the transient.
+     interactively.  The model transient supports Ollama,
+     OpenAI-compatible providers, and providers with a ‘chat-model’
+     slot.  URL editing is available when the provider has a ‘url’ slot.
+     It can also set the maximum number of output tokens.  Use "Reset
+     model fields" to clear model, temperature, context-length, and
+     max-token overrides and let the provider use its defaults; reset
+     values are shown as ‘default’ in the transient.
    • ‘ellama-code-complete’: Complete selected code or code in the
      current buffer according to a provided change using Ellama.
    • ‘ellama-code-add’: Generate and insert new code based on
@@ -2652,47 +2653,47 @@ Tag Table:
 Node: Top1379
 Node: Installation3973
 Node: Commands8987
-Node: Keymap18270
-Node: Configuration21157
-Node: Session Provider Keys37730
-Node: Session Compaction39591
-Node: Image Input41885
-Node: Task Tool Subagents44030
-Node: Plan-and-Act Agent Loop47204
-Node: Edit Tool Shell Hooks49785
-Node: DLP for Tool Input/Output52078
-Node: SRT Filesystem Policy for Tools68077
-Node: Context Management73746
-Node: Transient Menus for Context Management74814
-Node: Managing the Context76493
-Node: Considerations77268
-Node: Minor modes77861
-Node: ellama-context-header-line-mode79849
-Node: ellama-context-header-line-global-mode80674
-Node: ellama-context-mode-line-mode81394
-Node: ellama-context-mode-line-global-mode82242
-Node: Ellama Session Header Line Mode82946
-Node: Enabling and Disabling83515
-Node: Customization83962
-Node: Ellama Session Mode Line Mode84250
-Node: Enabling and Disabling (1)84835
-Node: Customization (1)85282
-Node: Using Blueprints85576
-Node: Key Components of Ellama Blueprints86216
-Node: Creating and Managing Blueprints86823
-Node: Blueprints files87801
-Node: Variable Management88222
-Node: Keymap and Mode88675
-Node: Transient Menus89611
-Node: Running Blueprints programmatically90157
-Node: MCP Integration90744
-Node: Agent Skills91979
-Node: Directory Structure92342
-Node: Creating a Skill93369
-Node: How it works93744
-Node: Acknowledgments94135
-Node: Contributions94846
-Node: GNU Free Documentation License95520
+Node: Keymap18338
+Node: Configuration21225
+Node: Session Provider Keys37798
+Node: Session Compaction39659
+Node: Image Input41953
+Node: Task Tool Subagents44098
+Node: Plan-and-Act Agent Loop47272
+Node: Edit Tool Shell Hooks49853
+Node: DLP for Tool Input/Output52146
+Node: SRT Filesystem Policy for Tools68145
+Node: Context Management73814
+Node: Transient Menus for Context Management74882
+Node: Managing the Context76561
+Node: Considerations77336
+Node: Minor modes77929
+Node: ellama-context-header-line-mode79917
+Node: ellama-context-header-line-global-mode80742
+Node: ellama-context-mode-line-mode81462
+Node: ellama-context-mode-line-global-mode82310
+Node: Ellama Session Header Line Mode83014
+Node: Enabling and Disabling83583
+Node: Customization84030
+Node: Ellama Session Mode Line Mode84318
+Node: Enabling and Disabling (1)84903
+Node: Customization (1)85350
+Node: Using Blueprints85644
+Node: Key Components of Ellama Blueprints86284
+Node: Creating and Managing Blueprints86891
+Node: Blueprints files87869
+Node: Variable Management88290
+Node: Keymap and Mode88743
+Node: Transient Menus89679
+Node: Running Blueprints programmatically90225
+Node: MCP Integration90812
+Node: Agent Skills92047
+Node: Directory Structure92410
+Node: Creating a Skill93437
+Node: How it works93812
+Node: Acknowledgments94203
+Node: Contributions94914
+Node: GNU Free Documentation License95588
 
 End Tag Table
 
diff --git a/tests/test-ellama-transient.el b/tests/test-ellama-transient.el
index 618f8104bf..ca727a24e1 100644
--- a/tests/test-ellama-transient.el
+++ b/tests/test-ellama-transient.el
@@ -33,6 +33,12 @@
 (require 'ert)
 (require 'llm-ollama)
 (require 'llm-openai)
+(require 'llm-provider-utils)
+
+(cl-defstruct (ellama-transient-test-provider
+               (:include llm-standard-chat-provider))
+  chat-model
+  url)
 
 (ert-deftest test-ellama-fill-transient-ollama-model-populates-fields ()
   (let ((provider (make-llm-ollama
@@ -96,6 +102,20 @@
     (should (= ellama-transient-temperature 0.4))
     (should (equal ellama-transient-url "http://127.0.0.1:8000/v1";))))
 
+(ert-deftest test-ellama-fill-transient-model-generic-provider ()
+  (let ((provider (make-ellama-transient-test-provider
+                   :chat-model "generic-model"
+                   :url "http://127.0.0.1:9000";
+                   :default-chat-temperature 0.25))
+        (ellama-transient-model-name "")
+        (ellama-transient-temperature 0.7)
+        (ellama-transient-url nil))
+    (ellama-fill-transient-model provider)
+    (should (eq ellama-transient-provider provider))
+    (should (equal ellama-transient-model-name "generic-model"))
+    (should (= ellama-transient-temperature 0.25))
+    (should (equal ellama-transient-url "http://127.0.0.1:9000";))))
+
 (ert-deftest test-ellama-transient-reset-model-fields-and-descriptions ()
   (let ((ellama-transient-model-name "model")
         (ellama-transient-temperature 0.4)
@@ -235,6 +255,30 @@
       (should (let ((key (llm-openai-compatible-key provider)))
                 (equal (if (functionp key) (funcall key) key) "secret"))))))
 
+(ert-deftest test-ellama-construct-provider-from-transient-generic-provider ()
+  (let ((base (make-ellama-transient-test-provider
+               :chat-model "old-model"
+               :url "http://old";
+               :default-chat-temperature 0.1))
+        (ellama-transient-model-name "new-model")
+        (ellama-transient-temperature 0.8)
+        (ellama-transient-url "http://new";)
+        (ellama-transient-provider nil))
+    (let ((provider (ellama-construct-provider-from-transient base)))
+      (should (ellama-transient-test-provider-p provider))
+      (should-not (eq provider base))
+      (should (equal (ellama-transient-test-provider-chat-model provider)
+                     "new-model"))
+      (should (equal (ellama-transient-test-provider-url provider)
+                     "http://new";))
+      (should (= (ellama-transient-test-provider-default-chat-temperature
+                  provider)
+                 0.8))
+      (should (equal (ellama-transient-test-provider-chat-model base)
+                     "old-model"))
+      (should (equal (ellama-transient-test-provider-url base)
+                     "http://old";)))))
+
 (ert-deftest test-ellama-transient-provider-candidates-hide-provider-values ()
   (let* ((secret "transient-secret")
          (ellama-provider (make-llm-openai-compatible

Reply via email to