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