Skip to content

Commit 33f3a26

Browse files
committed
Fix chat history for tool calls
1 parent e6e159c commit 33f3a26

14 files changed

Lines changed: 408 additions & 52 deletions

File tree

src/eca/config.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
(set! *warn-on-reflection* true)
1717

18-
(def ^:private initial-config
18+
(def initial-config
1919
{:openaiApiKey nil
2020
:anthropicApiKey nil
2121
:rules []

src/eca/features/chat.clj

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,12 @@
9494
user-prompt message
9595
all-tools (f.tools/all-tools @db* config)
9696
received-msgs* (atom "")
97-
add-msg! (fn [msg]
98-
(swap! db* update-in [:chats chat-id :messages] (fnil conj []) msg))]
97+
add-to-history! (fn [msg & [append-to-last?]]
98+
(if append-to-last?
99+
(swap! db* update-in [:chats chat-id :messages] (fn [messages msg]
100+
(conj (pop messages)
101+
(update (last messages) :content str msg))) msg)
102+
(swap! db* update-in [:chats chat-id :messages] (fnil conj []) msg)))]
99103
(messenger/chat-content-received
100104
messenger
101105
{:chat-id chat-id
@@ -113,7 +117,7 @@
113117
:config config
114118
:tools all-tools
115119
:on-first-message-received (fn [_]
116-
(add-msg! {:role "user" :content user-prompt})
120+
(add-to-history! {:role "user" :content user-prompt})
117121
(messenger/chat-content-received
118122
messenger
119123
{:chat-id chat-id
@@ -142,7 +146,7 @@
142146
:title (:title msg)
143147
:url (:url msg)}})
144148
:finish (do
145-
(add-msg! {:role "assistant" :content @received-msgs*})
149+
(add-to-history! {:role "assistant" :content @received-msgs*})
146150
(messenger/chat-content-received
147151
messenger
148152
{:chat-id chat-id
@@ -161,7 +165,7 @@
161165
:argumentText argument
162166
:id id
163167
:manual-approval false}}))
164-
:on-tool-called (fn [{:keys [id name arguments]}]
168+
:on-tool-called (fn [{:keys [id name arguments] :as tool-call}]
165169
(messenger/chat-content-received
166170
messenger
167171
{:chat-id chat-id
@@ -173,6 +177,11 @@
173177
:id id
174178
:manual-approval false}})
175179
(let [result (f.tools/call-tool! name arguments @db* config)]
180+
(when-not (string/blank? @received-msgs*)
181+
(add-to-history! {:role "assistant" :content @received-msgs*})
182+
(reset! received-msgs* ""))
183+
(add-to-history! {:role "tool_call" :content tool-call})
184+
(add-to-history! {:role "tool_call_output" :content (assoc tool-call :output result)})
176185
(messenger/chat-content-received
177186
messenger
178187
{:chat-id chat-id
@@ -218,9 +227,9 @@
218227

219228
(defn query-context
220229
[{:keys [query contexts chat-id]}
221-
db*]
222-
(let [config (config/all @db*)
223-
all-subfiles-and-dirs (into []
230+
db*
231+
config]
232+
(let [all-subfiles-and-dirs (into []
224233
(comp
225234
(map :uri)
226235
(map shared/uri->filename)

src/eca/handlers.clj

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,26 @@
2121
{}
2222
ollama-models))))
2323

24-
(defn initialize [{:keys [db* messenger]} params]
24+
(defn initialize [{:keys [db* messenger config]} params]
2525
(logger/logging-task
2626
:eca/initialize
2727
(swap! db* assoc
2828
:client-info (:client-info params)
2929
:workspace-folders (:workspace-folders params)
3030
:client-capabilities (:capabilities params)
3131
:chat-behavior (or (-> params :initialization-options :chat-behavior) (:chat-behavior @db*)))
32-
(let [config (config/all @db*)]
33-
(initialize-extra-models! db* config)
34-
(future
35-
(f.mcp/initialize-servers-async!
36-
{:on-server-updated (fn [server]
37-
(messenger/mcp-server-updated messenger server))}
38-
db*
39-
config))
40-
{:models (keys (:models @db*))
41-
:chat-default-model (f.chat/default-model @db*)
42-
:chat-behaviors (:chat-behaviors @db*)
43-
:chat-default-behavior (:chat-default-behavior @db*)
44-
:chat-welcome-message (:welcomeMessage (:chat config))})))
32+
(initialize-extra-models! db* config)
33+
(future
34+
(f.mcp/initialize-servers-async!
35+
{:on-server-updated (fn [server]
36+
(messenger/mcp-server-updated messenger server))}
37+
db*
38+
config))
39+
{:models (keys (:models @db*))
40+
:chat-default-model (f.chat/default-model @db*)
41+
:chat-behaviors (:chat-behaviors @db*)
42+
:chat-default-behavior (:chat-default-behavior @db*)
43+
:chat-welcome-message (:welcomeMessage (:chat config))}))
4544

4645
(defn shutdown [{:keys [db*]}]
4746
(logger/logging-task
@@ -50,13 +49,12 @@
5049
(reset! db* db/initial-db)
5150
nil))
5251

53-
(defn chat-prompt [{:keys [messenger db*]} params]
52+
(defn chat-prompt [{:keys [messenger db* config]} params]
5453
(logger/logging-task
5554
:eca/chat-prompt
56-
(let [config (config/all @db*)]
57-
(f.chat/prompt params db* messenger config))))
55+
(f.chat/prompt params db* messenger config)))
5856

59-
(defn chat-query-context [{:keys [db*]} params]
57+
(defn chat-query-context [{:keys [db* config]} params]
6058
(logger/logging-task
6159
:eca/chat-query-context
62-
(f.chat/query-context params db*)))
60+
(f.chat/query-context params db* config)))

src/eca/llm_providers/anthropic.clj

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,31 @@
5757
(fn [e]
5858
(on-error {:exception e})))))
5959

60+
(defn ^:private ->messages-with-history [past-messages user-prompt]
61+
(conj (mapv (fn [{:keys [role content] :as msg}]
62+
(case role
63+
"tool_call" {:role "assistant"
64+
:content [{:type "tool_use"
65+
:id (:id content)
66+
:name (:name content)
67+
:input (:arguments content)}]}
68+
69+
"tool_call_output"
70+
{:role "user"
71+
:content [{:type "tool_result"
72+
:tool-use-id (:id content)
73+
:content (llm-util/stringfy-tool-result content)}]}
74+
msg))
75+
past-messages)
76+
{:role "user" :content user-prompt}))
77+
6078
(defn completion!
6179
[{:keys [model user-prompt temperature context max-tokens
6280
api-key past-messages tools web-search]
6381
:or {max-tokens 1024
6482
temperature 1.0}}
6583
{:keys [on-message-received on-error on-prepare-tool-call on-tool-called]}]
66-
(let [messages (conj (vec past-messages) {:role "user" :content user-prompt})
84+
(let [messages (->messages-with-history past-messages user-prompt)
6785
body {:model model
6886
:messages messages
6987
:max_tokens max-tokens

src/eca/llm_providers/ollama.clj

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,21 @@
6464
:function (select-keys tool [:name :description :parameters])})
6565
tools))
6666

67+
(defn ^:private ->messages-with-history [context past-messages user-prompt]
68+
(concat
69+
[{:role "system" :content context}]
70+
(mapv (fn [{:keys [role content] :as msg}]
71+
(case role
72+
"tool_call" {:role "assistant" :tool-calls [{:type "function"
73+
:function content}]}
74+
"tool_call_output" {:role "tool" :content (llm-util/stringfy-tool-result content)}
75+
msg))
76+
past-messages)
77+
[{:role "user" :content user-prompt}]))
78+
6779
(defn completion! [{:keys [model user-prompt context host port past-messages tools]}
6880
{:keys [on-message-received on-error on-prepare-tool-call on-tool-called]}]
69-
(let [messages (concat
70-
[{:role "system" :content context}]
71-
past-messages
72-
[{:role "user" :content user-prompt}])
81+
(let [messages (->messages-with-history context past-messages user-prompt)
7382
body {:model model
7483
:messages messages
7584
:think false

src/eca/llm_providers/openai.clj

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,26 @@
4747
(fn [e]
4848
(on-error {:exception e})))))
4949

50+
(defn ^:private ->input-with-history [past-messages user-prompt]
51+
(conj (mapv (fn [{:keys [role content] :as msg}]
52+
(case role
53+
"tool_call" {:type "function_call"
54+
:name (:name content)
55+
:call-id (:id content)
56+
:arguments (:arguments content)}
57+
"tool_call_output"
58+
{:type "function_call_output"
59+
:name (:name content)
60+
:call-id (:id content)
61+
:output (llm-util/stringfy-tool-result content)}
62+
msg))
63+
past-messages)
64+
{:role "user" :content user-prompt}))
65+
5066
(defn completion! [{:keys [model user-prompt context temperature api-key past-messages tools web-search]
5167
:or {temperature 1.0}}
5268
{:keys [on-message-received on-error on-prepare-tool-call on-tool-called on-reason]}]
53-
(let [input (conj (vec past-messages) {:role "user" :content user-prompt})
69+
(let [input (->input-with-history past-messages user-prompt)
5470
tools (cond-> tools
5571
web-search (conj {:type "web_search_preview"}))
5672
body {:model model

src/eca/llm_util.clj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@
4949
[]
5050
(str (rand-int 9999)))
5151

52+
(defn stringfy-tool-result [result]
53+
(reduce
54+
#(str %1 (:content %2) "\n")
55+
""
56+
(-> result :output :contents)))
57+
5258
(defn log-request [tag rid url body]
5359
(logger/debug tag (format "[%s] Sending body: '%s', url: '%s'" rid body url)))
5460

src/eca/server.clj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
(ns eca.server
22
(:require
33
[clojure.core.async :as async]
4+
[eca.config :as config]
45
[eca.db :as db]
56
[eca.handlers :as handlers]
67
[eca.logger :as logger]
@@ -25,25 +26,28 @@
2526
(shutdown-agents)
2627
(System/exit 0)))
2728

29+
(defn ^:private with-config [components]
30+
(assoc components :config (config/all @(:db* components))))
31+
2832
(defmethod lsp.server/receive-request "initialize" [_ {:keys [server] :as components} params]
2933
(when-let [parent-process-id (:process-id params)]
3034
(liveness-probe/start! parent-process-id log-wrapper-fn #(exit server)))
31-
(handlers/initialize components params))
35+
(handlers/initialize (with-config components) params))
3236

3337
(defmethod lsp.server/receive-notification "initialized" [_ _components _params]
3438
(logger/info logger-tag "Initialized!"))
3539

3640
(defmethod lsp.server/receive-request "shutdown" [_ components _params]
37-
(handlers/shutdown components))
41+
(handlers/shutdown (with-config components)))
3842

3943
(defmethod lsp.server/receive-notification "exit" [_ {:keys [server]} _params]
4044
(exit server))
4145

4246
(defmethod lsp.server/receive-request "chat/prompt" [_ components params]
43-
(handlers/chat-prompt components params))
47+
(handlers/chat-prompt (with-config components) params))
4448

4549
(defmethod lsp.server/receive-request "chat/queryContext" [_ components params]
46-
(handlers/chat-query-context components params))
50+
(handlers/chat-query-context (with-config components) params))
4751

4852
(defn ^:private monitor-server-logs [log-ch]
4953
;; NOTE: if this were moved to `initialize`, after timbre has been configured,

0 commit comments

Comments
 (0)