|
164 | 164 | (swap! tool-call-by-id* update-in [id :args] str arguments-text) |
165 | 165 | (send-content! chat-ctx :assistant |
166 | 166 | (assoc-some |
167 | | - {:type :toolCallPrepare |
168 | | - :name name |
169 | | - :origin (tool-name->origin name all-tools) |
170 | | - :arguments-text (get-in @tool-call-by-id* [id :args]) |
171 | | - :id id |
172 | | - :manual-approval manual-approval?} |
173 | | - :summary (f.tools/tool-call-summary all-tools name nil)))) |
174 | | - :on-tool-called (fn [{:keys [id name arguments] :as tool-call}] |
175 | | - (assert-chat-not-stopped! chat-ctx) |
176 | | - (let [approved?* (promise) |
177 | | - details (f.tools/get-tool-call-details name arguments) |
178 | | - summary (f.tools/tool-call-summary all-tools name arguments)] |
179 | | - (send-content! chat-ctx :assistant |
180 | | - (assoc-some |
181 | | - {:type :toolCallRun |
182 | | - :name name |
183 | | - :origin (tool-name->origin name all-tools) |
184 | | - :arguments arguments |
185 | | - :id id |
186 | | - :manual-approval manual-approval?} |
187 | | - :details details |
188 | | - :summary summary)) |
189 | | - (swap! db* assoc-in [:chats chat-id :tool-calls id :approved?*] approved?*) |
190 | | - (when-not (string/blank? @received-msgs*) |
191 | | - (add-to-history! {:role "assistant" :content @received-msgs*}) |
192 | | - (reset! received-msgs* "")) |
193 | | - (if manual-approval? |
194 | | - (send-content! chat-ctx :system |
195 | | - {:type :progress |
196 | | - :state :running |
197 | | - :text "Waiting for tool call approval"}) |
198 | | - ;; Otherwise auto approve |
199 | | - (deliver approved?* true)) |
200 | | - (if @approved?* |
201 | | - (let [result (f.tools/call-tool! name arguments @db* config)] |
202 | | - (add-to-history! {:role "tool_call" :content tool-call}) |
203 | | - (add-to-history! {:role "tool_call_output" :content (assoc tool-call :output result)}) |
204 | | - (send-content! chat-ctx :assistant |
205 | | - (assoc-some |
206 | | - {:type :toolCalled |
207 | | - :origin (tool-name->origin name all-tools) |
| 167 | + {:type :toolCallPrepare |
208 | 168 | :name name |
209 | | - :arguments arguments |
210 | | - :error (:error result) |
211 | | - :id id |
212 | | - :outputs (:contents result)} |
213 | | - :details details |
214 | | - :summary summary))) |
215 | | - (do |
216 | | - (add-to-history! {:role "tool_call" :content tool-call}) |
217 | | - (add-to-history! {:role "tool_call_output" :content (assoc tool-call :output {:error true |
218 | | - :contents [{:text "Tool call rejected by user" |
219 | | - :type :text}]})}) |
220 | | - (send-content! chat-ctx :assistant |
221 | | - (assoc-some |
222 | | - {:type :toolCallRejected |
223 | 169 | :origin (tool-name->origin name all-tools) |
224 | | - :name name |
225 | | - :arguments arguments |
226 | | - :reason :user |
227 | | - :id id} |
228 | | - :details details |
229 | | - :summary summary)))) |
230 | | - (swap! tool-call-by-id* dissoc id) |
231 | | - (send-content! chat-ctx :system {:type :progress :state :running :text "Generating"}) |
232 | | - {:new-messages (get-in @db* [:chats chat-id :messages])})) |
| 170 | + :arguments-text (get-in @tool-call-by-id* [id :args]) |
| 171 | + :id id |
| 172 | + :manual-approval manual-approval?} |
| 173 | + :summary (f.tools/tool-call-summary all-tools name nil)))) |
| 174 | + :on-tools-called (fn [tool-calls] |
| 175 | + (assert-chat-not-stopped! chat-ctx) |
| 176 | + ;; Flush any pending assistant text once before processing multiple tool calls |
| 177 | + (when-not (string/blank? @received-msgs*) |
| 178 | + (add-to-history! {:role "assistant" :content @received-msgs*}) |
| 179 | + (reset! received-msgs* "")) |
| 180 | + (let [calls (doall |
| 181 | + (for [{:keys [id name arguments] :as tool-call} tool-calls] |
| 182 | + (let [approved?* (promise) |
| 183 | + details (f.tools/get-tool-call-details name arguments) |
| 184 | + summary (f.tools/tool-call-summary all-tools name arguments)] |
| 185 | + ;; Inform UI the tool is about to run and store approval promise |
| 186 | + (send-content! chat-ctx :assistant |
| 187 | + (assoc-some |
| 188 | + {:type :toolCallRun |
| 189 | + :name name |
| 190 | + :origin (tool-name->origin name all-tools) |
| 191 | + :arguments arguments |
| 192 | + :id id |
| 193 | + :manual-approval manual-approval?} |
| 194 | + :details details |
| 195 | + :summary summary)) |
| 196 | + (swap! db* assoc-in [:chats chat-id :tool-calls id :approved?*] approved?*) |
| 197 | + (if manual-approval? |
| 198 | + (send-content! chat-ctx :system |
| 199 | + {:type :progress |
| 200 | + :state :running |
| 201 | + :text "Waiting for tool call approval"}) |
| 202 | + ;; Otherwise auto approve |
| 203 | + (deliver approved?* true)) |
| 204 | + ;; Execute each tool call concurrently |
| 205 | + (future |
| 206 | + (if @approved?* |
| 207 | + (let [result (f.tools/call-tool! name arguments @db* config)] |
| 208 | + (add-to-history! {:role "tool_call" :content tool-call}) |
| 209 | + (add-to-history! {:role "tool_call_output" :content (assoc tool-call :output result)}) |
| 210 | + (send-content! chat-ctx :assistant |
| 211 | + (assoc-some |
| 212 | + {:type :toolCalled |
| 213 | + :origin (tool-name->origin name all-tools) |
| 214 | + :name name |
| 215 | + :arguments arguments |
| 216 | + :error (:error result) |
| 217 | + :id id |
| 218 | + :outputs (:contents result)} |
| 219 | + :details details |
| 220 | + :summary summary))) |
| 221 | + (do |
| 222 | + (add-to-history! {:role "tool_call" :content tool-call}) |
| 223 | + (add-to-history! {:role "tool_call_output" |
| 224 | + :content (assoc tool-call :output {:error true |
| 225 | + :contents [{:text "Tool call rejected by user" |
| 226 | + :type :text}]})}) |
| 227 | + (send-content! chat-ctx :assistant |
| 228 | + (assoc-some |
| 229 | + {:type :toolCallRejected |
| 230 | + :origin (tool-name->origin name all-tools) |
| 231 | + :name name |
| 232 | + :arguments arguments |
| 233 | + :reason :user |
| 234 | + :id id} |
| 235 | + :details details |
| 236 | + :summary summary)))) |
| 237 | + (swap! tool-call-by-id* dissoc id)))))] |
| 238 | + ;; Wait all tool calls to complete before returning |
| 239 | + (run! deref calls) |
| 240 | + (send-content! chat-ctx :system {:type :progress :state :running :text "Generating"}) |
| 241 | + {:new-messages (get-in @db* [:chats chat-id :messages])})) |
233 | 242 | :on-reason (fn [{:keys [status id text external-id]}] |
234 | 243 | (assert-chat-not-stopped! chat-ctx) |
235 | 244 | (case status |
|
0 commit comments