Skip to content

Commit 3b889eb

Browse files
committed
Add totalTimeMs to reason and toolCall content blocks.
1 parent 821d376 commit 3b889eb

File tree

10 files changed

+49
-22
lines changed

10 files changed

+49
-22
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+
- Add `totalTimeMs` to reason and toolCall content blocks.
6+
57
## 0.48.0
68

79
- Add nix flake build.

docs/protocol.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,11 @@ interface ReasonFinishedContent {
468468
* The id of this reason
469469
*/
470470
id: string;
471+
472+
/**
473+
* The total time the reason took in milliseconds.
474+
*/
475+
totalTimeMs: number;
471476
}
472477

473478
/**
@@ -681,6 +686,11 @@ interface ToolCalledContent {
681686
text: string;
682687
}];
683688

689+
/**
690+
* The total time the call took in milliseconds.
691+
*/
692+
totalTimeMs: number;
693+
684694
/**
685695
* Summary text to present about this tool call,
686696
* ex: 'Reading file "foo"...'.

integration-test/entrypoint.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[llm-mock.server :as llm-mock.server]))
66

77
(def namespaces
8-
'[integration.initialize-test
8+
'[;integration.initialize-test
99
integration.chat.openai-test
1010
integration.chat.anthropic-test
1111
integration.chat.github-copilot-test

integration-test/integration/chat/anthropic_test.clj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})
133133
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text "I should say"})
134134
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text " hello"})
135-
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?)})
135+
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?) :totalTimeMs (m/pred number?)})
136136
(match-content chat-id "assistant" {:type "text" :text "hello"})
137137
(match-content chat-id "assistant" {:type "text" :text " there!"})
138138
(match-content chat-id "system" {:type "usage"
@@ -165,7 +165,7 @@
165165
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})
166166
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text "I should say"})
167167
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text " fine"})
168-
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?)})
168+
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?) :totalTimeMs (m/pred number?)})
169169
(match-content chat-id "assistant" {:type "text" :text "I'm "})
170170
(match-content chat-id "assistant" {:type "text" :text " fine"})
171171
(match-content chat-id "system" {:type "usage"
@@ -209,7 +209,7 @@
209209
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})
210210
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text "I should call tool"})
211211
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text " eca_directory_tree"})
212-
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?)})
212+
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?) :totalTimeMs (m/pred number?)})
213213
(match-content chat-id "assistant" {:type "text" :text "I will list files"})
214214
(match-content chat-id "assistant" {:type "toolCallPrepare"
215215
:origin "native"
@@ -252,6 +252,7 @@
252252
:name "eca_directory_tree"
253253
:arguments {:path (h/project-path->canon-path "resources")}
254254
:summary "Listing file tree"
255+
:totalTimeMs (m/pred number?)
255256
:error false
256257
:outputs [{:type "text" :text (str "├── file1.md\n"
257258
"└── file2.md\n\n"

integration-test/integration/chat/github_copilot_test.clj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})
124124
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text "I should say"})
125125
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text " hello"})
126-
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?)})
126+
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?) :totalTimeMs (m/pred number?)})
127127
(match-content chat-id "assistant" {:type "text" :text "hello"})
128128
(match-content chat-id "assistant" {:type "text" :text " there!"})
129129
(match-content chat-id "system" {:type "usage"
@@ -158,7 +158,7 @@
158158
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})
159159
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text "I should say"})
160160
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text " fine"})
161-
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?)})
161+
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?) :totalTimeMs (m/pred number?)})
162162
(match-content chat-id "assistant" {:type "text" :text "I'm "})
163163
(match-content chat-id "assistant" {:type "text" :text " fine"})
164164
(match-content chat-id "system" {:type "usage"
@@ -207,7 +207,7 @@
207207
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})
208208
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text "I should call tool"})
209209
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text " eca_directory_tree"})
210-
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?)})
210+
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?) :totalTimeMs (m/pred number?)})
211211
(match-content chat-id "assistant" {:type "text" :text "I will list files"})
212212
(match-content chat-id "assistant" {:type "toolCallPrepare"
213213
:origin "native"
@@ -248,6 +248,7 @@
248248
:id "tool-1"
249249
:name "eca_directory_tree"
250250
:arguments {:path (h/project-path->canon-path "resources")}
251+
:totalTimeMs number?
251252
:summary "Listing file tree"})
252253
(match-content chat-id "assistant" {:type "toolCalled"
253254
:origin "native"

integration-test/integration/chat/ollama_test.clj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@
134134
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})
135135
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text "I should say"})
136136
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text " hello"})
137-
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?)})
137+
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?) :totalTimeMs (m/pred number?)})
138138
(match-content chat-id "assistant" {:type "text" :text "hello"})
139139
(match-content chat-id "assistant" {:type "text" :text " there!"})
140140
(match-content chat-id "system" {:type "progress" :state "finished"})
@@ -164,7 +164,7 @@
164164
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})
165165
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text "I should say"})
166166
(match-content chat-id "assistant" {:type "reasonText" :id (m/pred string?) :text " fine"})
167-
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?)})
167+
(match-content chat-id "assistant" {:type "reasonFinished" :id (m/pred string?) :totalTimeMs (m/pred number?)})
168168
(match-content chat-id "assistant" {:type "text" :text "I'm "})
169169
(match-content chat-id "assistant" {:type "text" :text " fine"})
170170
(match-content chat-id "system" {:type "progress" :state "finished"})
@@ -233,6 +233,7 @@
233233
:name "eca_directory_tree"
234234
:arguments {:path (h/project-path->canon-path "resources")}
235235
:summary "Listing file tree"
236+
:totalTimeMs (m/pred number?)
236237
:error false
237238
:outputs [{:type "text" :text (str "├── file1.md\n"
238239
"└── file2.md\n\n"

integration-test/integration/chat/openai_test.clj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
(match-content chat-id "assistant" {:type "reasonStarted" :id "123"})
133133
(match-content chat-id "assistant" {:type "reasonText" :id "123" :text "I should say"})
134134
(match-content chat-id "assistant" {:type "reasonText" :id "123" :text " hello"})
135-
(match-content chat-id "assistant" {:type "reasonFinished" :id "123"})
135+
(match-content chat-id "assistant" {:type "reasonFinished" :id "123" :totalTimeMs (m/pred number?)})
136136
(match-content chat-id "assistant" {:type "text" :text "hello"})
137137
(match-content chat-id "assistant" {:type "text" :text " there!"})
138138
(match-content chat-id "system" {:type "usage"
@@ -165,7 +165,7 @@
165165
(match-content chat-id "assistant" {:type "reasonStarted" :id "234"})
166166
(match-content chat-id "assistant" {:type "reasonText" :id "234" :text "I should say"})
167167
(match-content chat-id "assistant" {:type "reasonText" :id "234" :text " fine"})
168-
(match-content chat-id "assistant" {:type "reasonFinished" :id "234"})
168+
(match-content chat-id "assistant" {:type "reasonFinished" :id "234" :totalTimeMs (m/pred number?)})
169169
(match-content chat-id "assistant" {:type "text" :text "I'm "})
170170
(match-content chat-id "assistant" {:type "text" :text " fine"})
171171
(match-content chat-id "system" {:type "usage"
@@ -209,7 +209,7 @@
209209
(match-content chat-id "assistant" {:type "reasonStarted" :id "123"})
210210
(match-content chat-id "assistant" {:type "reasonText" :id "123" :text "I should call tool"})
211211
(match-content chat-id "assistant" {:type "reasonText" :id "123" :text " eca_directory_tree"})
212-
(match-content chat-id "assistant" {:type "reasonFinished" :id "123"})
212+
(match-content chat-id "assistant" {:type "reasonFinished" :id "123" :totalTimeMs (m/pred number?)})
213213
(match-content chat-id "assistant" {:type "text" :text "I will list files"})
214214
(match-content chat-id "assistant" {:type "toolCallPrepare"
215215
:origin "native"
@@ -252,6 +252,7 @@
252252
:name "eca_directory_tree"
253253
:arguments {:path (h/project-path->canon-path "resources")}
254254
:summary "Listing file tree"
255+
:totalTimeMs (m/pred number?)
255256
:error false
256257
:outputs [{:type "text" :text (str "├── file1.md\n"
257258
"└── file2.md\n\n"

src/eca/features/chat.clj

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132

133133
[:execution-approved :execution-start]
134134
{:status :executing
135-
:actions [:send-toolCallRunning]}
135+
:actions [:set-start-time :send-toolCallRunning]}
136136

137137
[:executing :execution-end]
138138
{:status :completed
@@ -216,6 +216,7 @@
216216
:name (:name event-data)
217217
:arguments (:arguments event-data)
218218
:error (:error event-data)
219+
:total-time-ms (:total-time-ms event-data)
219220
:outputs (:outputs event-data)}
220221
:details (:details event-data)
221222
:summary (:summary event-data)))
@@ -254,6 +255,10 @@
254255
(swap! db* assoc-in [:chats (:chat-id chat-ctx) :tool-calls tool-call-id :decision-reason]
255256
(:reason event-data))
256257

258+
:set-start-time
259+
(swap! db* assoc-in [:chats (:chat-id chat-ctx) :tool-calls tool-call-id :start-time]
260+
(:start-time event-data))
261+
257262
;; Logging actions
258263
:log-rejection
259264
(logger/info logger-tag "Tool call rejected"
@@ -379,7 +384,7 @@
379384
past-messages (get-in db [:chats chat-id :messages] [])
380385
all-tools (f.tools/all-tools behavior @db* config)
381386
received-msgs* (atom "")
382-
received-thinking* (atom "")
387+
reasonings* (atom {})
383388
add-to-history! (fn [msg]
384389
(swap! db* update-in [:chats chat-id :messages] (fnil conj []) msg))]
385390
(when-let [expires-at (get-in db [:auth provider :expires-at])]
@@ -494,11 +499,13 @@
494499
{:origin origin
495500
:name name
496501
:arguments arguments
502+
:start-time (System/currentTimeMillis)
497503
:details details
498504
:summary summary})
499505
;; assert: In :executing
500506
(let [result (f.tools/call-tool! name arguments @db* config messenger behavior)
501-
details (f.tools/tool-call-details-after-invocation name arguments details result)]
507+
details (f.tools/tool-call-details-after-invocation name arguments details result)
508+
{:keys [start-time]} (get-tool-call-state @db* chat-id id)]
502509
(add-to-history! {:role "tool_call" :content (assoc tool-call
503510
:details details
504511
:summary summary
@@ -515,6 +522,7 @@
515522
:arguments arguments
516523
:error (:error result)
517524
:outputs (:contents result)
525+
:total-time-ms (- (System/currentTimeMillis) start-time)
518526
:details details
519527
:summary summary})))
520528
;; assert: In :rejected state
@@ -540,21 +548,24 @@
540548
:on-reason (fn [{:keys [status id text external-id]}]
541549
(assert-chat-not-stopped! chat-ctx)
542550
(case status
543-
:started (send-content! chat-ctx :assistant
544-
{:type :reasonStarted
545-
:id id})
551+
:started (do
552+
(swap! reasonings* assoc-in [id :start-time] (System/currentTimeMillis))
553+
(send-content! chat-ctx :assistant
554+
{:type :reasonStarted
555+
:id id}))
546556
:thinking (do
547-
(swap! received-thinking* str text)
557+
(swap! reasonings* update-in [id :text] str text)
548558
(send-content! chat-ctx :assistant
549559
{:type :reasonText
550560
:id id
551561
:text text}))
552562
:finished (do
553563
(add-to-history! {:role "reason" :content {:id id
554564
:external-id external-id
555-
:text @received-thinking*}})
565+
:text (get-in @reasonings* [id :text])}})
556566
(send-content! chat-ctx :assistant
557567
{:type :reasonFinished
568+
:total-time-ms (- (System/currentTimeMillis) (get-in @reasonings* [id :start-time]))
558569
:id id}))
559570
nil))
560571
:on-error (fn [{:keys [message exception]}]

test/eca/features/chat_test.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@
212212
{:role :assistant :content {:type :toolCallPrepare :id "call-1" :name "list_allowed_directories" :arguments-text ""}}
213213
{:role :assistant :content {:type :toolCallRun :id "call-1" :name "list_allowed_directories" :arguments {} :manual-approval false}}
214214
{:role :assistant :content {:type :toolCallRunning :id "call-1" :name "list_allowed_directories" :arguments {}}}
215-
{:role :assistant :content {:type :toolCalled :id "call-1" :name "list_allowed_directories" :arguments {} :outputs [{:content "Allowed directories: /foo/bar" :type :text}]}}
215+
{:role :assistant :content {:type :toolCalled :id "call-1" :name "list_allowed_directories" :arguments {} :total-time-ms number? :outputs [{:content "Allowed directories: /foo/bar" :type :text}]}}
216216
{:role :system :content {:type :progress :state :running :text "Generating"}}
217217
{:role :assistant :content {:type :text :text "I can see: \n"}}
218218
{:role :assistant :content {:type :text :text "/foo/bar"}}

test/eca/features/chat_tool_call_state_test.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@
408408
(let [result (#'f.chat/transition-tool-call! db* chat-ctx tool-call-id :execution-start
409409
{:name "list_files" :origin "filesystem" :arguments {:path "/tmp"}})]
410410
(is (match? {:status :executing
411-
:actions [:send-toolCallRunning]}
411+
:actions [:set-start-time :send-toolCallRunning]}
412412
result)
413413
"Expected transition to :executing status with no additional actions")
414414

0 commit comments

Comments
 (0)