branch: externals/llm
commit 5c5513083669f84c2119e6cfdfd754fb0660158c
Author: Andrew Hyatt <ahy...@gmail.com>
Commit: GitHub <nore...@github.com>

    Add streaming tool calls for Ollama (#199)
    
    Partially fixes https://github.com/ahyatt/llm/issues/198
    
    ---------
    
    Co-authored-by: Andrew Hyatt <ahy...@continua.ai>
---
 NEWS.org      |  2 ++
 llm-ollama.el | 60 +++++++++++++++++++++++++++++++++++------------------------
 2 files changed, 38 insertions(+), 24 deletions(-)

diff --git a/NEWS.org b/NEWS.org
index 43a7631be1..ca626ab994 100644
--- a/NEWS.org
+++ b/NEWS.org
@@ -1,6 +1,8 @@
 * Version 0.26.1
 - Add Claude 4 models
 - Fix error using Open AI for batch embeddings
+- Add streaming tool calls for Ollama
+- Fix Ollama tool-use booleans
 * Version 0.26.0
 - Call tools with =nil= when called with false JSON values.
 - Fix bug in ollama batch embedding generation.
diff --git a/llm-ollama.el b/llm-ollama.el
index ddf13f53f6..ef708c21cb 100644
--- a/llm-ollama.el
+++ b/llm-ollama.el
@@ -186,9 +186,6 @@ These are just the text inside the tag, not the tag 
itself."))
                            (llm-chat-prompt-interactions prompt))))
     (setq request-plist (plist-put request-plist :messages messages))
     (setq request-plist (plist-put request-plist :model (llm-ollama-chat-model 
provider)))
-    (when (and streaming (llm-chat-prompt-tools prompt))
-      (signal 'not-implemented
-              "Ollama does not support streaming with function calls"))
     (when (llm-chat-prompt-tools prompt)
       (setq request-plist (plist-put
                            request-plist :tools
@@ -228,7 +225,8 @@ These are just the text inside the tag, not the tag 
itself."))
    (vconcat (mapcar (lambda (tool-use)
                       `(:function (:name ,(llm-provider-utils-tool-use-name 
tool-use)
                                          :arguments ,(json-serialize
-                                                      
(llm-provider-utils-tool-use-args tool-use)))))
+                                                      
(llm-provider-utils-tool-use-args tool-use)
+                                                      :false-object 
:json-false))))
                     tool-uses))))
 
 (cl-defmethod llm-provider-streaming-media-handler ((_ llm-ollama) receiver _)
@@ -236,25 +234,39 @@ These are just the text inside the tag, not the tag 
itself."))
         (plz-media-type:application/x-ndjson
          :handler (let ((in-reasoning))
                     (lambda (data)
-                      (when-let ((response (assoc-default
-                                            'content
-                                            (assoc-default 'message data))))
-                        ;; The response from ollama should just have the tag 
and
-                        ;; nothing more.
-                        (cond
-                         ((string-match (rx
-                                         (seq "<"
-                                              (eval `(or 
,@llm-ollama-reasoning-tags))
-                                              ">")) response)
-                          (setq in-reasoning t))
-                         ((string-match (rx
-                                         (seq "</"
-                                              (eval `(or 
,@llm-ollama-reasoning-tags))
-                                              ">")) response)
-                          (setq in-reasoning nil))
-                         (t (funcall receiver (list (if in-reasoning
-                                                        :reasoning
-                                                      :text) response))))))))))
+                      (let* ((message (assoc-default 'message data))
+                             (text (assoc-default 'content message))
+                             (tool-call (assoc-default 'tool_calls message))
+                             (response nil))
+                        (when (and text (> (length text) 0))
+                          ;; The response from ollama should just have the tag 
and
+                          ;; nothing more.
+                          (cond
+                           ((string-match (rx
+                                           (seq "<"
+                                                (eval `(or 
,@llm-ollama-reasoning-tags))
+                                                ">")) text)
+                            (setq in-reasoning t))
+                           ((string-match (rx
+                                           (seq "</"
+                                                (eval `(or 
,@llm-ollama-reasoning-tags))
+                                                ">")) text)
+                            (setq in-reasoning nil))
+                           (t
+                            (setq response
+                                  (plist-put response (if in-reasoning 
:reasoning :text) text)))))
+                        (when tool-call
+                          (setq response
+                                (plist-put response :tool-uses-raw
+                                           (aref tool-call 0))))
+                        (funcall receiver response)))))))
+
+(cl-defmethod llm-provider-collect-streaming-tool-uses ((_ llm-ollama) data)
+  (mapcar (lambda (fc) (let ((f-alist (cdr fc)))
+                         (make-llm-provider-utils-tool-use
+                          :name (assoc-default 'name f-alist)
+                          :args (assoc-default 'arguments f-alist))))
+          data))
 
 (cl-defmethod llm-name ((provider llm-ollama))
   (or (llm-ollama-chat-model provider)
@@ -265,7 +277,7 @@ These are just the text inside the tag, not the tag 
itself."))
                                         2048))
 
 (cl-defmethod llm-capabilities ((provider llm-ollama))
-  (append '(streaming json-response model-list)
+  (append '(streaming streaming-tool-use json-response model-list)
           (when (and (llm-ollama-embedding-model provider)
                      (let ((embedding-model (llm-models-match
                                              (llm-ollama-embedding-model 
provider))))

Reply via email to