branch: externals/ellama
commit a9df4be69371cede1196d3fb7c67eb8267923326
Merge: 968b8631d1 73d07c3652
Author: Sergey Kostyaev <s-kosty...@users.noreply.github.com>
Commit: GitHub <nore...@github.com>

    Merge pull request #131 from s-kostyaev/solve-reasoning-problem-chain
    
    Add solve reasoning problem chain
---
 README.org |  7 ++++++
 ellama.el  | 83 ++++++++++++++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 71 insertions(+), 19 deletions(-)

diff --git a/README.org b/README.org
index 21a8a7a336..89a531cce4 100644
--- a/README.org
+++ b/README.org
@@ -229,6 +229,13 @@ Chat translation enable.
 
 Chat translation disable.
 
+*** ellama-solve-reasoning-problem
+
+Solve reasoning problem with [[https://arxiv.org/pdf/2406.12442][Absctraction 
of Thought]] technique. It
+uses a chain of multiple messages to LLM and help it to provide much
+better answers on reasoning problems. Even small LLMs like 
[[https://ollama.com/library/phi3][phi3-mini]]
+provides much better results on reasoning tasks using AoT.
+
 ** Keymap
 
 Here is a table of keybindings and their associated functions in
diff --git a/ellama.el b/ellama.el
index ee5220074c..de3ae60526 100644
--- a/ellama.el
+++ b/ellama.el
@@ -1193,8 +1193,8 @@ file by default.
 :on-error ON-ERROR -- ON-ERROR a function that's called with an error message 
on
 failure (with BUFFER current).
 
-:on-done ON-DONE -- ON-DONE a function that's called with the full response 
text
-when the request completes (with BUFFER current)."
+:on-done ON-DONE -- ON-DONE a function or list of functions that's called with
+ the full response text when the request completes (with BUFFER current)."
   (let* ((session (plist-get args :session))
         (provider (if session
                       (ellama-session-provider session)
@@ -1267,7 +1267,10 @@ when the request completes (with BUFFER current)."
                                    (with-current-buffer buffer
                                      (accept-change-group ellama--change-group)
                                      (spinner-stop)
-                                     (funcall donecb text)
+                                     (if (listp donecb)
+                                         (mapc (lambda (fn) (funcall fn text))
+                                               donecb)
+                                       (funcall donecb text))
                                      (setq ellama--current-request nil)
                                      (ellama-request-mode -1)))
                                  (lambda (_ msg)
@@ -1278,14 +1281,15 @@ when the request completes (with BUFFER current)."
                                      (setq ellama--current-request nil)
                                      (ellama-request-mode -1)))))))))
 
-(defun ellama-chain (initial-prompt forms)
+(defun ellama-chain (initial-prompt forms &optional acc)
   "Call chain of FORMS on INITIAL-PROMPT.
+ACC will collect responses in reverse order (previous answer will be on top).
 Each form is a plist that can contain different options:
 
 :provider PROVIDER - use PROVIDER instead of `ellama-provider'.
 
 :transform FUNCTION - use FUNCTION to transform result of previous step to new
-prompt.
+prompt.  FUCTION will be called with two arguments INITIAL-PROMPT and ACC.
 
 :session SESSION - use SESSION in current step.
 
@@ -1296,12 +1300,14 @@ last step only.
   (let* ((hd (car forms))
         (tl (cdr forms))
         (provider (or (plist-get hd :provider) ellama-provider))
-        (transform (or (plist-get hd :transform) #'identity))
-        (prompt (apply transform (list initial-prompt)))
+        (transform (plist-get hd :transform))
+        (prompt (if transform
+                    (apply transform (list initial-prompt acc))
+                  initial-prompt))
         (session (plist-get hd :session))
         (chat (plist-get hd :chat))
-        (show (or (plist-get hd :show) ellama-always-show-chain-steps chat))
-        (buf (if (or (and tl (not chat)) (not session))
+        (show (or (plist-get hd :show) ellama-always-show-chain-steps))
+        (buf (if (or (and (not chat)) (not session))
                  (get-buffer-create (make-temp-name
                                      (ellama-generate-name provider 
real-this-command prompt)))
                (ellama-get-session-buffer ellama--current-session-id))))
@@ -1310,7 +1316,13 @@ last step only.
     (with-current-buffer buf
       (funcall ellama-major-mode))
     (if chat
-       (ellama-chat prompt nil :provider provider)
+       (ellama-chat
+        prompt
+        nil
+        :provider provider
+        :on-done (lambda (res)
+                   (when tl
+                     (ellama-chain res tl (cons res acc)))))
       (ellama-stream
        prompt
        :provider provider
@@ -1320,18 +1332,44 @@ last step only.
                 #'ellama--translate-markdown-to-org-filter)
        :on-done (lambda (res)
                  (when tl
-                   (ellama-chain res tl)))))))
+                   (ellama-chain res tl (cons res acc))))))))
 
-(defun ellama-chat-done (text)
+;;;###autoload
+(defun ellama-solve-reasoning-problem (problem)
+  "Solve reasoning PROBLEM with absctraction of thought.
+Problem will be solved with the chain of questions to LLM."
+  (interactive "sProblem: ")
+  (ellama-chain
+   problem
+   '((:chat t
+           :transform (lambda (problem _)
+                        (format "Problem:
+%s
+
+Let's think logically and provide abstract higher order plan how to solve this 
kind
+of problems. Don't dive into small details only provide high-level plan." 
problem)))
+     (:chat t
+           :transform (lambda (_ _)
+                        "Provide more detailed plan. On what details should we 
pay attention?"))
+     (:chat t
+           :transform (lambda (_ _)
+                        "Now revise the plan and provide the final solution."))
+     (:chat t
+           :transform (lambda (_ _)
+                        "Provide short final answer based on final 
solution.")))))
+
+(defun ellama-chat-done (text &optional on-done)
   "Chat done.
-Will call `ellama-chat-done-callback' on TEXT."
+Will call `ellama-chat-done-callback' and ON-DONE on TEXT."
   (save-excursion
     (goto-char (point-max))
     (insert "\n\n")
     (when ellama-session-auto-save
       (save-buffer)))
   (when ellama-chat-done-callback
-    (funcall ellama-chat-done-callback text)))
+    (funcall ellama-chat-done-callback text))
+  (when on-done
+    (funcall on-done text)))
 
 (defun ellama--translate-generated-text-on-done (translation-buffer)
   "Translate generated text into TRANSLATION-BUFFER."
@@ -1399,7 +1437,10 @@ Will call `ellama-chat-done-callback' on TEXT."
 If CREATE-SESSION set, creates new session even if there is an active session.
 ARGS contains keys for fine control.
 
-:provider PROVIDER -- PROVIDER is an llm provider for generation."
+:provider PROVIDER -- PROVIDER is an llm provider for generation.
+
+:on-done ON-DONE -- ON-DONE a function that's called with
+the full response text when the request completes (with BUFFER current)."
   (interactive "sAsk ellama: ")
   (let* ((providers (append
                      `(("default model" . ellama-provider)
@@ -1407,10 +1448,13 @@ ARGS contains keys for fine control.
                            '("ollama model" . 
(ellama-get-ollama-local-model))))
                      ellama-providers))
         (variants (mapcar #'car providers))
+        (donecb (plist-get args :on-done))
         (provider (if current-prefix-arg
-                      (eval (alist-get
-                             (completing-read "Select model: " variants)
-                             providers nil nil #'string=))
+                      (progn
+                        (setq current-prefix-arg nil)
+                        (eval (alist-get
+                               (completing-read "Select model: " variants)
+                               providers nil nil #'string=)))
                     (or (plist-get args :provider)
                         ellama-provider)))
         (session (if (or create-session
@@ -1451,7 +1495,8 @@ ARGS contains keys for fine control.
                  (ellama-get-nick-prefix-for-mode) " " ellama-assistant-nick 
":\n")
          (ellama-stream prompt
                         :session session
-                        :on-done #'ellama-chat-done
+                        :on-done (if donecb (list 'ellama-chat-done donecb)
+                                   'ellama-chat-done)
                         :filter (when (derived-mode-p 'org-mode)
                                   
#'ellama--translate-markdown-to-org-filter)))))))
 

Reply via email to