branch: externals/llm
commit 566c630f356a46f98039e739bf9f1cb332c5c60a
Author: Andrew Hyatt <[email protected]>
Commit: GitHub <[email protected]>
Normalize false values when tools are called with or return false (#242)
All false values must be `:json`, when we serialize.
---
NEWS.org | 1 +
llm-provider-utils-test.el | 20 +++++++++++++++++++-
llm-provider-utils.el | 43 +++++++++++++++++++++++++------------------
3 files changed, 45 insertions(+), 19 deletions(-)
diff --git a/NEWS.org b/NEWS.org
index 9659cfdade..f57d2af55d 100644
--- a/NEWS.org
+++ b/NEWS.org
@@ -1,5 +1,6 @@
* Version 0.29.0
- Check for tool use mismatches and define new errors for them
+- Normalize false values in tool args or tool call results
* Version 0.28.5
- Improved the tool calling docs
- Fix for running tools in the original buffer with streaming
diff --git a/llm-provider-utils-test.el b/llm-provider-utils-test.el
index 4c6c0f3ca3..53d7aa30bc 100644
--- a/llm-provider-utils-test.el
+++ b/llm-provider-utils-test.el
@@ -68,6 +68,22 @@
(should (equal (llm-provider-utils-convert-to-serializable '(:inner '(:a foo
:b bar)))
'(:inner '(:a "foo" :b "bar")))))
+(ert-deftest llm-provider-utils-append-to-prompt ()
+ (let ((prompt (llm-make-chat-prompt "Prompt")))
+ (llm-provider-utils-append-to-prompt prompt '(:a 1 :b :json-false)
+ (list
+ (make-llm-chat-prompt-tool-result
+ :tool-name "tool"
+ :result :json-false)))
+ (should (equal (nth 1 (llm-chat-prompt-interactions prompt))
+ (make-llm-chat-prompt-interaction
+ :role 'tool-results
+ :content "(:a 1 :b nil)"
+ :tool-results (list
+ (make-llm-chat-prompt-tool-result
+ :tool-name "tool"
+ :result :false)))))))
+
(ert-deftest llm-provider-utils-combine-to-system-prompt ()
(let* ((interaction1 (make-llm-chat-prompt-interaction :role 'user :content
"Hello"))
(example1 (cons "Request 1" "Response 1"))
@@ -159,7 +175,9 @@
(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)))))
+ (llm-provider-utils--normalize-args '(:a 1 :b :json-false))))
+ (should (equal '((a . 1) (b . nil))
+ (llm-provider-utils--normalize-args '((a . 1) (b .
:json-false))))))
(cl-defstruct llm-testing-provider (llm-standard-chat-provider) ())
diff --git a/llm-provider-utils.el b/llm-provider-utils.el
index c776e79bc4..c28ee0b8ef 100644
--- a/llm-provider-utils.el
+++ b/llm-provider-utils.el
@@ -1,6 +1,6 @@
;;; llm-provider-utils.el --- Functions to make building providers easier -*-
lexical-binding: t; package-lint-main-file: "llm.el"; ;
byte-compile-docstring-max-column: 200-*-
-;; Copyright (c) 2023-2025 Free Software Foundation, Inc.
+;; Copyright (c) 2023-2026 Free Software Foundation, Inc.
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
@@ -741,9 +741,18 @@ ROLE will be `assistant' by default, but can be passed in
for other roles."
:content (if (or (not output)
(and (not (stringp output))
(not tool-results)))
- output
- (format "%s" output))
- :tool-results tool-results)))))
+ (llm-provider-utils--normalize-args output
:false)
+ (format
+ "%s"
+ (llm-provider-utils--normalize-args
output)))
+ :tool-results (mapcar
+ (lambda (r)
+ (setf
(llm-chat-prompt-tool-result-result r)
+
(llm-provider-utils--normalize-args
+
(llm-chat-prompt-tool-result-result r)
+ :false))
+ r)
+ tool-results))))))
(defun llm-provider-utils-process-result (provider prompt partial-result
multi-output success-callback
error-callback)
@@ -815,22 +824,18 @@ This transforms the plist so that:
value)
value))))
-(defun llm-provider-utils--normalize-args (args)
+(defun llm-provider-utils--normalize-args (args &optional false-val)
"Normalize ARGS to a form that can be passed to the user.
-This will convert all :json-false and :false values to nil."
+This will convert all :json-false and :false values to FALSE-VAL."
(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)
+ ((vectorp args) (vconcat (mapcar (lambda (a)
+ (llm-provider-utils--normalize-args a
false-val))
+ args)))
+ ((consp args) (cons
+ (llm-provider-utils--normalize-args (car args) false-val)
+ (llm-provider-utils--normalize-args (cdr args) false-val)))
+ ((member args '(:json-false :false)) false-val)
(t args)))
(defun llm-provider-utils-execute-tool-uses (provider prompt tool-uses
multi-output
@@ -859,7 +864,9 @@ have returned results."
(cl-loop
for tool-use in tool-uses do
(let* ((name (llm-provider-utils-tool-use-name tool-use))
- (arguments (llm-provider-utils-tool-use-args tool-use))
+ (arguments
+ (llm-provider-utils--normalize-args
+ (llm-provider-utils-tool-use-args tool-use)))
(tool (or
(seq-find
(lambda (f) (equal name (llm-tool-name f)))