Skip to content

Commit 8eb55cb

Browse files
authored
Merge pull request #109 from bombaywalla/add-toolreject-params
Add missing parameters to toolCallRejected
2 parents 714ef76 + 90d4e68 commit 8eb55cb

File tree

3 files changed

+72
-29
lines changed

3 files changed

+72
-29
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- Added missing parameters to `toolCallRejected` where possible. PR #109
6+
57
## 0.49.0
68

79
- Add `totalTimeMs` to reason and toolCall content blocks.

src/eca/features/chat.clj

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@
8686
- promise init & delivery
8787
- logging/metrics
8888
89-
Note: all actions are run in the order specified.
89+
Note: all actions are run in the order specified. So, generally, the :send-* actions should be last.
90+
Note: The :status is updated before any actions are run, so the actions have the latest :status.
9091
9192
Note: all choices (i.e. conditionals) have to be made in code and result
9293
in different events sent to the state machine.
@@ -95,15 +96,15 @@
9596
{;; Note: transition-tool-call! treats no existing state as :initial state
9697
[:initial :tool-prepare]
9798
{:status :preparing
98-
:actions [:send-toolCallPrepare :init-decision-reason]}
99+
:actions [:init-tool-call-state :send-toolCallPrepare]}
99100

100101
[:preparing :tool-prepare]
101102
{:status :preparing
102103
:actions [:send-toolCallPrepare]} ; Multiple prepares allowed
103104

104105
[:preparing :tool-run]
105106
{:status :check-approval
106-
:actions [:init-approval-promise :send-toolCallRun]}
107+
:actions [:init-arguments :init-approval-promise :send-toolCallRun]}
107108
;; TODO: What happens if the promise is created, but no deref happens since the call is stopped?
108109

109110
[:check-approval :approval-ask]
@@ -222,16 +223,20 @@
222223
:summary (:summary event-data)))
223224

224225
:send-toolCallRejected
225-
(send-content! chat-ctx :assistant
226-
(assoc-some
227-
{:type :toolCallRejected
228-
:id tool-call-id
229-
:origin (:origin event-data)
230-
:name (:name event-data)
231-
:arguments (:arguments event-data)
232-
:reason (:code (:reason event-data) :user)}
233-
:details (:details event-data)
234-
:summary (:summary event-data)))
226+
(let [tool-call-state (get-tool-call-state @db* (:chat-id chat-ctx) tool-call-id)
227+
name (:name tool-call-state)
228+
origin (:origin tool-call-state)
229+
arguments (:arguments tool-call-state)]
230+
(send-content! chat-ctx :assistant
231+
(assoc-some
232+
{:type :toolCallRejected
233+
:id tool-call-id
234+
:origin (or (:origin event-data) origin)
235+
:name (or (:name event-data) name)
236+
:arguments (or (:arguments event-data) arguments)
237+
:reason (:code (:reason event-data) :user)}
238+
:details (:details event-data)
239+
:summary (:summary event-data))))
235240

236241
;; State management actions
237242
:init-approval-promise
@@ -246,10 +251,21 @@
246251
(deliver (get-in @db* [:chats (:chat-id chat-ctx) :tool-calls tool-call-id :approved?*])
247252
true)
248253

249-
:init-decision-reason
250-
(swap! db* assoc-in [:chats (:chat-id chat-ctx) :tool-calls tool-call-id :decision-reason]
251-
{:reason {:code :none
252-
:text "No reason"}})
254+
:init-tool-call-state
255+
(swap! db* update-in [:chats (:chat-id chat-ctx) :tool-calls tool-call-id] assoc
256+
;; :status is initialized by the state transition machinery
257+
;; :approval* is initialized by the :init-approval-promise action
258+
;; :arguments is initialized by the :init-arguments action
259+
;; :start-time is initialized by the :set-start-time action
260+
:name (:name event-data)
261+
:arguments (:arguments event-data)
262+
:origin (:origin event-data)
263+
:decision-reason {:code :none
264+
:text "No reason"})
265+
266+
:init-arguments
267+
(swap! db* assoc-in [:chats (:chat-id chat-ctx) :tool-calls tool-call-id :arguments]
268+
(:arguments event-data))
253269

254270
:set-decision-reason
255271
(swap! db* assoc-in [:chats (:chat-id chat-ctx) :tool-calls tool-call-id :decision-reason]
@@ -475,8 +491,8 @@
475491
:manual-approval ask?
476492
:details details
477493
:summary summary}))
478-
;; assert: In: :check-approval or :stopped
479-
(when-not (#{:stopped} (:status (get-tool-call-state @db* chat-id id)))
494+
;; assert: In: :check-approval or :stopped or :rejected
495+
(when-not (#{:stopped :rejected} (:status (get-tool-call-state @db* chat-id id)))
480496
(case approval
481497
:ask (transition-tool-call! db* chat-ctx id :approval-ask
482498
{:state :running

test/eca/features/chat_tool_call_state_test.clj

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
(let [result (#'f.chat/transition-tool-call! db* chat-ctx tool-call-id :tool-prepare event-data)]
122122

123123
(is (match? {:status :preparing
124-
:actions [:send-toolCallPrepare :init-decision-reason]}
124+
:actions [:init-tool-call-state :send-toolCallPrepare]}
125125
result)
126126
"Expected return value to show :preparing status and send toolCallPrepare action")
127127

@@ -218,7 +218,7 @@
218218
(let [result (#'f.chat/transition-tool-call! db* chat-ctx tool-call-id :tool-run run-event-data)]
219219

220220
(is (match? {:status :check-approval
221-
:actions [:init-approval-promise :send-toolCallRun]}
221+
:actions [:init-arguments :init-approval-promise :send-toolCallRun]}
222222
result)
223223
"Expected next state to be :check-approval with actions of :init-approval-promise and :send-toolCallRun")
224224

@@ -273,7 +273,7 @@
273273
;; Step 2: :preparing -> :check-approval
274274
(let [result (#'f.chat/transition-tool-call! db* chat-ctx tool-call-id :tool-run run-event-data)]
275275
(is (match? {:status :check-approval
276-
:actions [:init-approval-promise :send-toolCallRun]}
276+
:actions [:init-arguments :init-approval-promise :send-toolCallRun]}
277277
result)
278278
"Expected transition to :check-approval with init promise and send run actions")
279279

@@ -517,6 +517,7 @@
517517
(#'f.chat/transition-tool-call! db* chat-ctx "tool-rejected" :stop-requested))
518518
"Expected exception as already rejected tool calls cannot be stopped"))))))
519519

520+
;; TODO: This test and the previous test seem to be testing similar things. Clean up.
520521
(deftest transition-tool-call-stop-transitions-test
521522
(testing "Stop transitions from various states"
522523
(h/reset-components!)
@@ -600,7 +601,14 @@
600601
"Expected system message to contain 'stopped' text")
601602

602603
(is (< 0 (count tool-reject-messages))
603-
"Expected at least one toolCallRejected notification to be sent for active tool calls")))))
604+
"Expected at least one toolCallRejected notification to be sent for active tool calls")
605+
606+
(is (every? (comp :id :content) tool-reject-messages)
607+
"Expected every toolCallRejected message to have an :id")
608+
(is (every? (comp :name :content) tool-reject-messages)
609+
"Expected every toolCallRejected message to have a :name")
610+
(is (every? (comp :origin :content) tool-reject-messages)
611+
"Expected every toolCallRejected message to have an :origin")))))
604612

605613
(deftest test-stop-prompt-message-count
606614
;; Test that stop-prompt sends appropriate number of messages
@@ -629,21 +637,38 @@
629637
(h/reset-components!)
630638
(let [db* (h/db*)
631639
chat-id "test-chat"
632-
chat-ctx {:chat-id chat-id :request-id "req-123" :messenger (h/messenger)}]
640+
tool-call-id "tool-call-1"
641+
chat-ctx {:chat-id chat-id :request-id "req-123" :messenger (h/messenger)}
642+
approved?* (promise)]
633643

634644
(swap! db* assoc-in [:chats chat-id]
635645
{:status :running
636646
:current-request-id "req-123"})
637647

638-
(#'f.chat/transition-tool-call! db* chat-ctx "tool-1" :tool-prepare
648+
(#'f.chat/transition-tool-call! db* chat-ctx tool-call-id :tool-prepare
639649
{:name "list_files" :origin "filesystem" :arguments-text "{}"})
640-
650+
(#'f.chat/transition-tool-call! db* chat-ctx tool-call-id :tool-run
651+
{:approved?* approved?*
652+
:name "list_files"
653+
:origin "filesystem"
654+
:arguments {"id" 123 "value" 42}})
641655
(f.chat/prompt-stop {:chat-id chat-id} db* (h/messenger))
656+
(when-not @approved?*
657+
(#'f.chat/transition-tool-call! db* chat-ctx tool-call-id :send-reject
658+
{:approved?* approved?*
659+
:name "list_files"
660+
:origin "filesystem"
661+
:arguments {"id" 123 "value" 42}}))
642662

643663
(let [chat-messages (:chat-content-received (h/messages))
644-
message-types (map #(get-in % [:content :type]) chat-messages)]
645-
(is (some #{:toolCallRejected} message-types)
646-
"Expected toolCallRejected notifications when stopping with active tool calls")))))
664+
tool-call-rejected-messages (filter #(= :toolCallRejected (get-in % [:content :type]))
665+
chat-messages)]
666+
667+
(is (< 0 (count tool-call-rejected-messages))
668+
"Expected toolCallRejected notifications when stopping with active tool calls")
669+
670+
(is (some #(get-in % [:content :arguments] %) tool-call-rejected-messages)
671+
"Expected at least one toolCallRejected message to include non-nil :arguments")))))
647672

648673
(deftest test-stop-prompt-with-non-running-chat
649674
;; Test stop-prompt behavior when chat is not running.

0 commit comments

Comments
 (0)