branch: externals/llm commit d3b46890f3d081044a4f3422f5280b27fb356f5d Author: Andrew Hyatt <ahy...@gmail.com> Commit: GitHub <nore...@github.com>
Return nil for false values in tool calls. (#191) This should fix https://github.com/ahyatt/llm/issues/173. --- NEWS.org | 2 ++ README.org | 2 ++ llm-integration-test.el | 15 +++++++++++++++ llm-provider-utils-test.el | 12 ++++++++++++ llm-provider-utils.el | 22 +++++++++++++++++++++- llm.el | 2 +- 6 files changed, 53 insertions(+), 2 deletions(-) diff --git a/NEWS.org b/NEWS.org index d7970e0278..3cdbd24dd9 100644 --- a/NEWS.org +++ b/NEWS.org @@ -1,3 +1,5 @@ +* Version 0.26.0 +- Call tools with =nil= when called with false JSON values * Version 0.25.0 - Add =llm-ollama-authed= provider, which is like Ollama but takes a key. - Set Gemini 2.5 Pro to be the default Gemini model diff --git a/README.org b/README.org index a510937688..4469a584bd 100644 --- a/README.org +++ b/README.org @@ -271,6 +271,8 @@ The various chat APIs will execute the functions defined in =tools= slot with th After the tool is called, the client could use the result, but if you want to proceed with the conversation, or get a textual response that accompany the function you should just send the prompt back with no modifications. This is because the LLM gives the tool use to perform, and then expects to get back the results of that tool use. The results were already executed at the end of the call which returned the tools used, which also stores the result of that execution in the prompt. Th [...] +Tools will be called with vectors for array results, =nil= for false boolean results, and plists for objects. + Be aware that there is no gaurantee that the tool will be called correctly. While the LLMs mostly get this right, they are trained on Javascript functions, so imitating Javascript names is recommended. So, "write_email" is a better name for a function than "write-email". Examples can be found in =llm-tester=. There is also a function call to generate function calls from existing elisp functions in =utilities/elisp-to-tool.el=. diff --git a/llm-integration-test.el b/llm-integration-test.el index bfc1631c95..8f7bfdd324 100644 --- a/llm-integration-test.el +++ b/llm-integration-test.el @@ -331,6 +331,21 @@ else. We really just want to see if it's in the right ballpark." ;; Test that we can send the function back to the provider without error. (llm-chat provider prompt)))) +(llm-def-integration-test llm-boolean-tool-use (provider) + (when (member 'tool-use (llm-capabilities provider)) + (llm-chat provider (llm-make-chat-prompt + "Is Lyon the capital of France?" + :tools + (list (llm-make-tool + :function (lambda (result) + (should-not result)) + :name "verifier" + :description "Test the LLM's decision on the veracity of the statement." + :args '((:name "llm-decision" + :description "The decision on the statement by the LLM." + :type boolean)) + :async nil)))))) + (llm-def-integration-test llm-tool-use-multi-output (provider) (when (member 'tool-use (llm-capabilities provider)) (let* ((prompt (llm-integration-test-tool-use-prompt)) diff --git a/llm-provider-utils-test.el b/llm-provider-utils-test.el index 06189ac8a6..faae8228cb 100644 --- a/llm-provider-utils-test.el +++ b/llm-provider-utils-test.el @@ -148,5 +148,17 @@ (should (equal '(:foo 3) (llm-provider-utils-streaming-accumulate '(:foo 1) '(:foo 2)))) (should (equal '(:foo "foo bar baz") (llm-provider-utils-streaming-accumulate '(:foo "foo bar") '(:foo " baz"))))) +(ert-deftest llm-provider-utils--normalize-args () + (should-not (llm-provider-utils--normalize-args :false)) + (should-not (llm-provider-utils--normalize-args :json-false)) + (should (equal '(1 2 nil) + (llm-provider-utils--normalize-args '(1 2 :json-false)))) + (should (equal [1 2 nil] + (llm-provider-utils--normalize-args [1 2 :json-false]))) + (should (equal '(1 2 [t nil t]) + (llm-provider-utils--normalize-args '(1 2 [t :false t])))) + (should (equal '(:a 1 :b nil) + (llm-provider-utils--normalize-args '(:a 1 :b :json-false))))) + (provide 'llm-provider-utils-test) ;;; llm-provider-utils-test.el ends here diff --git a/llm-provider-utils.el b/llm-provider-utils.el index 6fc1e113e5..1936744f2c 100644 --- a/llm-provider-utils.el +++ b/llm-provider-utils.el @@ -25,6 +25,7 @@ (require 'llm-request-plz) (require 'llm-models) (require 'seq) +(require 'compat) (cl-defstruct llm-standard-provider "A struct indicating that this is a standard provider. @@ -787,6 +788,24 @@ This transforms the plist so that: value) value)))) +(defun llm-provider-utils--normalize-args (args) + "Normalize ARGS to a form that can be passed to the user. + +This will convert all :json-false and :false values to nil." + (cond + ((vectorp args) (vconcat (mapcar #'llm-provider-utils--normalize-args args))) + ((listp args) (mapcar #'llm-provider-utils--normalize-args args)) + ((plistp args) (let (new-plist) + (map-do + (lambda (key value) + (setq new-plist + (plist-put new-plist + key + (llm-provider-utils--normalize-args value)))) + args))) + ((member args '(:json-false :false)) nil) + (t args))) + (defun llm-provider-utils-execute-tool-uses (provider prompt tool-uses multi-output partial-result success-callback) "Execute TOOL-USES, a list of `llm-provider-utils-tool-use'. @@ -841,7 +860,8 @@ have returned results." (if (llm-tool-async tool) (apply (llm-tool-function tool) (append (list end-func) call-args)) - (funcall end-func (apply (llm-tool-function tool) call-args))))))) + (funcall end-func (apply (llm-tool-function tool) + (llm-provider-utils--normalize-args call-args)))))))) ;; This is a useful method for getting out of the request buffer when it's time diff --git a/llm.el b/llm.el index 1008e12ee7..773be2ed18 100644 --- a/llm.el +++ b/llm.el @@ -4,7 +4,7 @@ ;; Author: Andrew Hyatt <ahy...@gmail.com> ;; Homepage: https://github.com/ahyatt/llm -;; Package-Requires: ((emacs "28.1") (plz "0.8") (plz-event-source "0.1.1") (plz-media-type "0.2.1")) +;; Package-Requires: ((emacs "28.1") (plz "0.8") (plz-event-source "0.1.1") (plz-media-type "0.2.1") (compat "29.1")) ;; Package-Version: 0.25.0 ;; SPDX-License-Identifier: GPL-3.0-or-later ;;