branch: elpa/gptel
commit 00f39ea179630cd9a15430d0edc2d584a139ed31
Author: Psionik K <73710933+psioni...@users.noreply.github.com>
Commit: Karthik Chikmagalur <karthikchikmaga...@gmail.com>

    gptel: Ignore blank/whitespace messages
    
    - Support across APIs for blank messages in the messages array
    varies.  Uniformly enforce a policy of not including
    blank/whitespace messages in the messages array.
    
    - When response tracking is off, All models trim the whole buffer
    using simple string-trim.
    
    - Zero-length multi-part vectors are also skipped.
    
    - Whitespace inside a prefix is respected.  That is, a prefix
    containing whitespace at either end will not be recognized as one
    if this whitespace is missing in the buffer.
    
    * gptel.el (gptel--trim-prefixes): `gptel--trim-prefixes' now
    returns nil whenever a string is empty after trimming (this will
    occur for blank strings or prompt-only strings).
    
    * gptel-anthropic.el: (gptel--parse-buffer,
    gptel--anthropic-parse-multipart): Omit empty strings when
    constructing the messages array.
    * gptel-gemini.el (gptel--parse-buffer,
    gptel--gemini-parse-multipart): Ditto.
    * gptel-ollama.el (gptel--parse-buffer,
    gptel--ollama-parse-multipart): Ditto.
    * gptel-openai.el (gptel--parse-buffer,
    gptel--openai-parse-multipart): Ditto.
---
 gptel-anthropic.el | 38 +++++++++++++++++++-------------------
 gptel-gemini.el    | 33 +++++++++++++++------------------
 gptel-ollama.el    | 31 ++++++++++++++-----------------
 gptel-openai.el    | 25 ++++++++++++-------------
 gptel.el           | 19 +++++++++++++------
 5 files changed, 73 insertions(+), 73 deletions(-)

diff --git a/gptel-anthropic.el b/gptel-anthropic.el
index 60a07cbe81..c97cbf5bc2 100644
--- a/gptel-anthropic.el
+++ b/gptel-anthropic.el
@@ -286,27 +286,27 @@ TOOL-USE is a list of plists containing tool names, 
arguments and call results."
             ;; XXX update for tools
             (pcase (get-char-property (point) 'gptel)
               ('response
-               (push (list :role "assistant"
-                           :content (buffer-substring-no-properties (point) 
prev-pt))
-                     prompts))
+               (when-let* ((content
+                            (gptel--trim-prefixes
+                             (buffer-substring-no-properties (point) 
prev-pt))))
+                 (when (not (string-blank-p content))
+                   (push (list :role "assistant" :content content) prompts))))
               ('nil                     ; user role: possibly with media
-               (if include-media       
-                   (push (list :role "user"
-                               :content
-                               (gptel--anthropic-parse-multipart
-                                (gptel--parse-media-links major-mode (point) 
prev-pt)))
-                         prompts)
-                 (push (list :role "user"
-                             :content
-                             (gptel--trim-prefixes
-                              (buffer-substring-no-properties (point) 
prev-pt)))
-                       prompts)))))
+               (if include-media
+                   (when-let* ((content (gptel--anthropic-parse-multipart
+                                         (gptel--parse-media-links major-mode 
(point) prev-pt))))
+                     (when (> (length content) 0)
+                       (push (list :role "user" :content content) prompts)))
+                 (when-let* ((content (gptel--trim-prefixes
+                                       (buffer-substring-no-properties (point) 
prev-pt))))
+                   (push (list :role "user" :content content) prompts))))))
           (setq prev-pt (point))
           (and max-entries (cl-decf max-entries)))
-      (push (list :role "user"
-                  :content
-                  (string-trim (buffer-substring-no-properties (point-min) 
(point-max))))
-            prompts))
+      (when-let* ((content (string-trim (buffer-substring-no-properties
+                                         (point-min) (point-max)))))
+        ;; XXX fails if content is empty.  The correct error behavior is left 
to
+        ;; a future discussion.
+        (push (list :role "user" :content content) prompts)))
     prompts))
 
 (defun gptel--anthropic-parse-multipart (parts)
@@ -329,7 +329,7 @@ format."
    for media = (plist-get part :media)
    if text do
    (and (or (= n 1) (= n last)) (setq text (gptel--trim-prefixes text))) and
-   unless (string-empty-p text)
+   if text
    collect `(:type "text" :text ,text) into parts-array end
    else if media
    do
diff --git a/gptel-gemini.el b/gptel-gemini.el
index 4018b3f021..e6311b6662 100644
--- a/gptel-gemini.el
+++ b/gptel-gemini.el
@@ -236,27 +236,24 @@ See generic implementation for full documentation."
                     (not (= (point) prev-pt)))
           (pcase (get-char-property (point) 'gptel)
             ('response
-             (push (list :role "model"
-                         :parts
-                         (list :text (buffer-substring-no-properties (point) 
prev-pt)))
-                   prompts))
+             (when-let* ((content (gptel--trim-prefixes
+                                   (buffer-substring-no-properties (point) 
prev-pt))))
+               (push (list :role "model" :parts (list :text content)) 
prompts)))
             ('nil
              (if include-media
-                 (push (list :role "user"
-                             :parts (gptel--gemini-parse-multipart
-                                     (gptel--parse-media-links major-mode 
(point) prev-pt)))
-                       prompts)
-               (push (list :role "user"
-                           :parts
-                           `[(:text ,(gptel--trim-prefixes
-                                      (buffer-substring-no-properties (point) 
prev-pt)))])
-                     prompts))))
+                 (when-let* ((content (gptel--gemini-parse-multipart
+                                       (gptel--parse-media-links major-mode 
(point) prev-pt))))
+                   (when (> (length content) 0)
+                     (push (list :role "user" :parts content) prompts)))
+               (when-let* ((content (gptel--trim-prefixes
+                                     (buffer-substring-no-properties
+                                      (point) prev-pt))))
+                 (push (list :role "user" :parts `[(:text ,content)]) 
prompts)))))
           (setq prev-pt (point))
           (and max-entries (cl-decf max-entries)))
-      (push (list :role "user"
-                  :parts
-                  `[(:text ,(string-trim (buffer-substring-no-properties 
(point-min) (point-max))))])
-            prompts))
+      (let ((content (string-trim (buffer-substring-no-properties
+                                   (point-min) (point-max)))))
+        (push (list :role "user" :parts `[(:text ,content)]) prompts)))
     prompts))
 
 (defun gptel--gemini-parse-multipart (parts)
@@ -277,7 +274,7 @@ format."
    for media = (plist-get part :media)
    if text do
    (and (or (= n 1) (= n last)) (setq text (gptel--trim-prefixes text))) and
-   unless (string-empty-p text)
+   if text
    collect (list :text text) into parts-array end
    else if media
    collect
diff --git a/gptel-ollama.el b/gptel-ollama.el
index 9061786dc1..cb62108b12 100644
--- a/gptel-ollama.el
+++ b/gptel-ollama.el
@@ -152,26 +152,23 @@ Store response metadata in state INFO."
                     (not (= (point) prev-pt)))
           (pcase (get-char-property (point) 'gptel)
             ('response
-             (push (list :role "assistant"
-                         :content (buffer-substring-no-properties (point) 
prev-pt))
-                   prompts))
+             (when-let* ((content (gptel--trim-prefixes
+                                   (buffer-substring-no-properties (point) 
prev-pt))))
+               (push (list :role "assistant" :content content) prompts)))
             ('nil
              (if include-media
-                 (push (append '(:role "user")
-                               (gptel--ollama-parse-multipart
-                                (gptel--parse-media-links major-mode (point) 
prev-pt)))
-                       prompts)
-               (push (list :role "user"
-                           :content
-                           (gptel--trim-prefixes
-                            (buffer-substring-no-properties (point) prev-pt)))
-                     prompts))))
+                 (when-let* ((content (gptel--ollama-parse-multipart
+                                       (gptel--parse-media-links major-mode 
(point) prev-pt))))
+                   (when (> (length content) 0)
+                     (push (append '(:role "user") content) prompts)))
+               (when-let* ((content (gptel--trim-prefixes 
(buffer-substring-no-properties
+                                                           (point) prev-pt))))
+                 (push (list :role "user" :content content) prompts)))))
           (setq prev-pt (point))
           (and max-entries (cl-decf max-entries)))
-      (push (list :role "user"
-                  :content
-                  (string-trim (buffer-substring-no-properties (point-min) 
(point-max))))
-            prompts))
+      (let ((content (string-trim (buffer-substring-no-properties
+                                   (point-min) (point-max)))))
+        (push (list :role "user" :content content) prompts)))
     prompts))
 
 (defun gptel--ollama-parse-multipart (parts)
@@ -192,7 +189,7 @@ format."
    for media = (plist-get part :media)
    if text do
    (and (or (= n 1) (= n last)) (setq text (gptel--trim-prefixes text))) and
-   unless (string-empty-p text)
+   if text
    collect text into text-array end
    else if media
    collect (gptel--base64-encode media) into media-array end
diff --git a/gptel-openai.el b/gptel-openai.el
index bb9311c894..ae9b206d68 100644
--- a/gptel-openai.el
+++ b/gptel-openai.el
@@ -371,18 +371,17 @@ Mutate state INFO with response metadata."
              (and max-entries (cl-decf max-entries))
              (if include-media
                  (when-let* ((content (gptel--openai-parse-multipart
-                                      (gptel--parse-media-links major-mode 
(point) prev-pt))))
-                   (push (list :role "user" :content content) prompts))
-               (when-let* ((content (gptel--trim-prefixes
-                                     (buffer-substring-no-properties
-                                      (point) prev-pt))))
-                 (unless (string-empty-p content)
-                   (push (list :role "user" :content content) prompts))))))
+                                       (gptel--parse-media-links major-mode
+                                                                 (point) 
prev-pt))))
+                   (when (> (length content) 0)
+                     (push (list :role "user" :content content) prompts)))
+               (when-let* ((content (gptel--trim-prefixes 
(buffer-substring-no-properties
+                                                           (point) prev-pt))))
+                 (push (list :role "user" :content content) prompts)))))
           (setq prev-pt (point)))
-      (when-let* ((content (gptel--trim-prefixes 
(buffer-substring-no-properties
-                                                  (point-min) (point-max)))))
-        (unless (string-empty-p content)
-          (push (list :role "user" :content content) prompts))))
+      (let ((content (string-trim (buffer-substring-no-properties
+                                    (point-min) (point-max)))))
+        (push (list :role "user" :content content) prompts)))
     prompts))
 
 ;; TODO This could be a generic function
@@ -403,8 +402,8 @@ format."
    for text = (plist-get part :text)
    for media = (plist-get part :media)
    if text do
-   (and (or (= n 1) (= n last)) (setq text (gptel--trim-prefixes text))) and
-   unless (string-empty-p text)
+   (and (or (= n 1) (= n last)) (setq text (gptel--trim-prefixes text)))
+   and if text
    collect `(:type "text" :text ,text) into parts-array end
    else if media
    collect
diff --git a/gptel.el b/gptel.el
index a6727a2fe9..bf90a0f91c 100644
--- a/gptel.el
+++ b/gptel.el
@@ -913,12 +913,19 @@ Note: This will move the cursor."
   (or (alist-get major-mode gptel-response-prefix-alist) ""))
 
 (defsubst gptel--trim-prefixes (s)
-  "Remove prompt/response prefixes from string S."
-  (string-trim s
-   (format "[\t\r\n ]*\\(?:%s\\)?[\t\r\n ]*"
-             (regexp-quote (gptel-prompt-prefix-string)))
-   (format "[\t\r\n ]*\\(?:%s\\)?[\t\r\n ]*"
-           (regexp-quote (gptel-response-prefix-string)))))
+  "Remove prompt/response prefixes from string S.
+
+Return nil if string collapses to empty string."
+  (let* ((trimmed (string-trim-left
+                   s (format "[\t\r\n ]*\\(?:%s\\)?[\t\r\n ]*"
+                             (regexp-quote
+                              (gptel-prompt-prefix-string)))))
+         (trimmed (string-trim-right
+                   trimmed (format "[\t\r\n ]*\\(?:%s\\)?[\t\r\n ]*"
+                                   (regexp-quote
+                                    (gptel-response-prefix-string))))))
+    (unless (string-empty-p trimmed)
+      trimmed)))
 
 (defsubst gptel--link-standalone-p (beg end)
   "Return non-nil if positions BEG and END are isolated.

Reply via email to