Skip to content

Commit f96631e

Browse files
committed
Add support for file change diffs on tool call.
1 parent 308ef37 commit f96631e

File tree

4 files changed

+82
-66
lines changed

4 files changed

+82
-66
lines changed

docs/protocol.md

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,12 @@ interface ToolCallPrepareContent {
574574
* Whether this call requires manual approval from the user.
575575
*/
576576
manualApproval: boolean;
577+
578+
/**
579+
* Extra details about this call.
580+
* Clients may use this to present different UX for this tool call.
581+
*/
582+
details?: TooCallRunDetails;
577583
}
578584

579585
/**
@@ -611,32 +617,6 @@ interface ToolCallRunContent {
611617
details?: TooCallRunDetails;
612618
}
613619

614-
type TooCallRunDetails = FileChangeDetails;
615-
616-
interface FileChangeDetails {
617-
type: 'fileChange';
618-
619-
/**
620-
* The file path of this file change
621-
*/
622-
path: string;
623-
624-
/**
625-
* The content diff of this file change
626-
*/
627-
diff: string;
628-
629-
/**
630-
* The count of lines added in this change.
631-
*/
632-
linesAdded: number;
633-
634-
/**
635-
* The count of lines removed in this change.
636-
*/
637-
linesRemoved: number;
638-
}
639-
640620
/**
641621
* Tool call result that LLM trigerred and was executed already.
642622
*/
@@ -679,6 +659,12 @@ interface ToolCalledContent {
679659
*/
680660
content: string;
681661
}];
662+
663+
/**
664+
* Extra details about this call.
665+
* Clients may use this to present different UX for this tool call.
666+
*/
667+
details?: TooCallRunDetails;
682668
}
683669

684670
interface ToolCallRejected {
@@ -705,9 +691,41 @@ interface ToolCallRejected {
705691
* The reason why this tool call was rejected
706692
*/
707693
reason: 'user';
694+
695+
/**
696+
* Extra details about this call.
697+
* Clients may use this to present different UX for this tool call.
698+
*/
699+
details?: TooCallRunDetails;
708700
}
709701

710702
type ToolCallOrigin = 'mcp' | 'native';
703+
704+
type TooCallRunDetails = FileChangeDetails;
705+
706+
interface FileChangeDetails {
707+
type: 'fileChange';
708+
709+
/**
710+
* The file path of this file change
711+
*/
712+
path: string;
713+
714+
/**
715+
* The content diff of this file change
716+
*/
717+
diff: string;
718+
719+
/**
720+
* The count of lines added in this change.
721+
*/
722+
linesAdded: number;
723+
724+
/**
725+
* The count of lines removed in this change.
726+
*/
727+
linesRemoved: number;
728+
}
711729
```
712730

713731
### Chat approve tool call (➡️)

src/eca/diff.clj

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@
4343
{:added (+ added changed)
4444
:removed (+ removed changed)
4545
:diff
46-
(unlines (DiffUtils/generateUnifiedDiff
47-
file
48-
file
49-
(lines original)
50-
patch
51-
3))})))
46+
(->> (DiffUtils/generateUnifiedDiff file file (lines original) patch 3)
47+
(drop 2) ;; removes file header
48+
unlines)})))

src/eca/features/chat.clj

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
all-tools (f.tools/all-tools @db* config)
152152
received-msgs* (atom "")
153153
received-thinking* (atom "")
154-
tool-call-args-by-id* (atom {})
154+
tool-call-by-id* (atom {:args {}})
155155
add-to-history! (fn [msg]
156156
(swap! db* update-in [:chats chat-id :messages] (fnil conj []) msg))]
157157

@@ -199,26 +199,27 @@
199199
(finish-chat-prompt! :idle chat-ctx))))
200200
:on-prepare-tool-call (fn [{:keys [id name arguments-text]}]
201201
(assert-chat-not-stopped! chat-ctx)
202-
(swap! tool-call-args-by-id* update id str arguments-text)
202+
(swap! tool-call-by-id* update-in [id :args] str arguments-text)
203203
(send-content! chat-ctx :assistant
204204
{:type :toolCallPrepare
205205
:name name
206206
:origin (tool-name->origin name all-tools)
207-
:arguments-text (get @tool-call-args-by-id* id)
207+
:arguments-text (get-in @tool-call-by-id* [id :args])
208208
:id id
209209
:manual-approval manual-approval?}))
210210
:on-tool-called (fn [{:keys [id name arguments] :as tool-call}]
211211
(assert-chat-not-stopped! chat-ctx)
212-
(send-content! chat-ctx :assistant
213-
(assoc-some
214-
{:type :toolCallRun
215-
:name name
216-
:origin (tool-name->origin name all-tools)
217-
:arguments arguments
218-
:id id
219-
:manual-approval manual-approval?}
220-
:details (f.tools/get-tool-call-details name arguments)))
221-
(let [approved?* (promise)]
212+
(let [approved?* (promise)
213+
details (f.tools/get-tool-call-details name arguments)]
214+
(send-content! chat-ctx :assistant
215+
(assoc-some
216+
{:type :toolCallRun
217+
:name name
218+
:origin (tool-name->origin name all-tools)
219+
:arguments arguments
220+
:id id
221+
:manual-approval manual-approval?}
222+
:details details))
222223
(swap! db* assoc-in [:chats chat-id :tool-calls id :approved?*] approved?*)
223224
(when-not (string/blank? @received-msgs*)
224225
(add-to-history! {:role "assistant" :content @received-msgs*})
@@ -234,34 +235,33 @@
234235
(let [result (f.tools/call-tool! name arguments @db* config)]
235236
(add-to-history! {:role "tool_call" :content tool-call})
236237
(add-to-history! {:role "tool_call_output" :content (assoc tool-call :output result)})
237-
(swap! tool-call-args-by-id* dissoc id)
238238
(send-content! chat-ctx :assistant
239-
{:type :toolCalled
240-
:origin (tool-name->origin name all-tools)
241-
:name name
242-
:arguments arguments
243-
:error (:error result)
244-
:id id
245-
:outputs (:contents result)})
246-
{:new-messages (get-in @db* [:chats chat-id :messages])})
239+
(assoc-some
240+
{:type :toolCalled
241+
:origin (tool-name->origin name all-tools)
242+
:name name
243+
:arguments arguments
244+
:error (:error result)
245+
:id id
246+
:outputs (:contents result)}
247+
:details details)))
247248
(do
248249
(add-to-history! {:role "tool_call" :content tool-call})
249250
(add-to-history! {:role "tool_call_output" :content (assoc tool-call :output {:contents [{:content "Tool call rejected by user"
250251
:error true
251252
:type :text}]})})
252-
(swap! tool-call-args-by-id* dissoc id)
253-
(send-content! chat-ctx :system
254-
{:type :progress
255-
:state :running
256-
:text "Generating"})
257253
(send-content! chat-ctx :assistant
258-
{:type :toolCallRejected
259-
:origin (tool-name->origin name all-tools)
260-
:name name
261-
:arguments arguments
262-
:reason :user
263-
:id id})
264-
{:new-messages (get-in @db* [:chats chat-id :messages])}))))
254+
(assoc-some
255+
{:type :toolCallRejected
256+
:origin (tool-name->origin name all-tools)
257+
:name name
258+
:arguments arguments
259+
:reason :user
260+
:id id}
261+
:details details))))
262+
(swap! tool-call-by-id* dissoc id)
263+
(send-content! chat-ctx :system {:type :progress :state :running :text "Generating"})
264+
{:new-messages (get-in @db* [:chats chat-id :messages])}))
265265
:on-reason (fn [{:keys [status id text external-id]}]
266266
(assert-chat-not-stopped! chat-ctx)
267267
(case status

test/eca/features/chat_test.clj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ for allowed directories and then list files"
242242
{:role :assistant :content {:type :toolCallPrepare :id "call-1" :name "list_allowed_directories" :arguments-text "" :manual-approval false}}
243243
{:role :assistant :content {:type :toolCallRun :id "call-1" :name "list_allowed_directories" :arguments {} :manual-approval false}}
244244
{:role :assistant :content {:type :toolCalled :id "call-1" :name "list_allowed_directories" :arguments {} :outputs [{:content "Allowed directories: /foo/bar" :type :text}]}}
245+
{:role :system :content {:type :progress :state :running :text "Generating"}}
245246
{:role :assistant :content {:type :text :text "I can see: \n"}}
246247
{:role :assistant :content {:type :text :text "/foo/bar"}}
247248
{:role :system :content {:state :finished :type :progress}}]}

0 commit comments

Comments
 (0)