Skip to content

Commit 30c9bc7

Browse files
committed
Fix past messages sent to LLMs
1 parent ea19915 commit 30c9bc7

File tree

10 files changed

+63
-79
lines changed

10 files changed

+63
-79
lines changed

docs/configuration.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,9 @@ For MCP servers configuration, use the `mcpServers` config, example:
7777
```javascript
7878
{
7979
"mcpServers": {
80-
"filesystem": {
81-
"command": "npx",
82-
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/greg/dev/eca-emacs"
83-
]
80+
"memory": {
81+
"command": "npx",
82+
"args": ["-y", "@modelcontextprotocol/server-memory"]
8483
}
8584
}
8685
}

src/eca/features/chat.clj

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,17 @@
132132
:past-messages past-messages
133133
:config config
134134
:tools all-tools
135-
:on-first-message-received (fn [_]
136-
(assert-chat-not-stopped! chat-id db* messenger)
137-
(add-to-history! {:role "user" :content user-prompt})
138-
(messenger/chat-content-received
139-
messenger
140-
{:chat-id chat-id
141-
:request-id request-id
142-
:role :system
143-
:content {:type :progress
144-
:state :running
145-
:text "Generating"}}))
135+
:on-first-response-received (fn [& _]
136+
(assert-chat-not-stopped! chat-id db* messenger)
137+
(add-to-history! {:role "user" :content user-prompt})
138+
(messenger/chat-content-received
139+
messenger
140+
{:chat-id chat-id
141+
:request-id request-id
142+
:role :system
143+
:content {:type :progress
144+
:state :running
145+
:text "Generating"}}))
146146
:on-message-received (fn [{:keys [type] :as msg}]
147147
(assert-chat-not-stopped! chat-id db* messenger)
148148
(case type

src/eca/features/tools.clj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
(when (get-in config [:nativeTools :shell :enabled])
3030
f.tools.shell/definitions))))
3131

32-
(defn all-tools [db config]
32+
(defn all-tools
33+
"Returns all available tools, including both native ECA tools
34+
(like filesystem and shell tools) and tools provided by MCP servers."
35+
[db config]
3336
(let [native-tools (concat
3437
[]
3538
(mapv #(select-keys % [:name :description :parameters])

src/eca/features/tools/filesystem.clj

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@
190190
(tools.util/single-text-content (format "Successfully moved %s to %s" source destination))))))
191191

192192
(def definitions
193-
{"list_directory"
193+
{"eca_list_directory"
194194
{:description (str "Get a detailed listing of all files and directories in a specified path. "
195195
"Results clearly distinguish between files and directories with [FILE] and [DIR] "
196196
"prefixes. This tool is essential for understanding directory structure and "
@@ -201,7 +201,7 @@
201201
:description "The absolute path to the directory to list."}}
202202
:required ["path"]}
203203
:handler #'list-directory}
204-
"read_file"
204+
"eca_read_file"
205205
{:description (str "Read the complete contents of a file from the file system. "
206206
"Handles various text encodings and provides detailed error messages "
207207
"if the file cannot be read. Use this tool when you need to examine "
@@ -218,7 +218,7 @@
218218
:description "If provided, returns only the last N lines of the file"}}
219219
:required ["path"]}
220220
:handler #'read-file}
221-
"write_file"
221+
"eca_write_file"
222222
{:description (str "Create a new file or completely overwrite an existing file with new content. " +
223223
"Use with caution as it will overwrite existing files without warning. " +
224224
"Handles text content with proper encoding. "
@@ -230,7 +230,7 @@
230230
:description "The content of the new file"}}
231231
:required ["path" "content"]}
232232
:handler #'write-file}
233-
"move_file"
233+
"eca_move_file"
234234
{:description (str "Move or rename files and directories. Can move files between directories "
235235
"and rename them in a single operation. If the destination exists, the "
236236
"operation will fail. Works across different directories and can be used "
@@ -243,7 +243,7 @@
243243
:description "The new absolute file path to move to."}}
244244
:required ["source" "destination"]}
245245
:handler #'move-file}
246-
"search_files"
246+
"eca_search_files"
247247
{:description (str "Recursively search for files and directories matching a pattern. "
248248
"Searches through all subdirectories from the starting path. The search "
249249
"is case-insensitive and matches partial names following java's FileSystem#getPathMatcher. Returns full paths to all "
@@ -257,7 +257,7 @@
257257
"Use '**' to match search in multiple levels like '**.txt'")}}
258258
:required ["path" "pattern"]}
259259
:handler #'search-files}
260-
"grep"
260+
"eca_grep"
261261
{:description (str "Fast content search tool that works with any codebase size. "
262262
"Finds the paths to files that have matching contents using regular expressions. "
263263
"Supports full regex syntax (eg. \"log.*Error\", \"function\\s+\\w+\", etc.). "
@@ -275,7 +275,7 @@
275275
:description "Maximum number of results to return (default: 1000)"}}
276276
:required ["path" "pattern"]}
277277
:handler #'grep}
278-
"replace_in_file"
278+
"eca_replace_in_file"
279279
{:description (str "Replace a specific string or content block in a file with new content. "
280280
"Finds the exact original content and replaces it with new content. "
281281
"Be extra careful to format the original-content exactly correctly, "

src/eca/features/tools/shell.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
(or (tools.util/invalid-arguments arguments [["working_directory" #(or (nil? %)
2020
(fs/exists? %)) "working directory $working_directory does not exist"]
2121
["commmand" (constantly (not (contains? exclude-cmds (first command-args)))) (format "Cannot run command '%s' because it is excluded by eca config."
22-
(first command-args))]])
22+
(first command-args))]])
2323
(let [work-dir (or (some-> user-work-dir fs/canonicalize str)
2424
(shared/uri->filename (:uri (first (:workspace-folders db)))))
2525
command-and-opts (concat [] command-args [:dir work-dir])
@@ -34,7 +34,7 @@
3434
(tools.util/single-text-content (str "Command failed with exit code " (:exit result) ": " (:err result)) :error))))))
3535

3636
(def definitions
37-
{"shell_command"
37+
{"eca_shell_command"
3838
{:description (str "Execute an arbitrary shell command and return the output. "
3939
"Useful to run commands like `ls`, `git status`, etc.")
4040
:parameters {:type "object"

src/eca/llm_api.clj

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,20 @@
4848
:type "function"))
4949

5050
(defn complete!
51-
[{:keys [model model-config context user-prompt config on-first-message-received
51+
[{:keys [model model-config context user-prompt config on-first-response-received
5252
on-message-received on-error on-prepare-tool-call on-tool-called on-reason
5353
past-messages tools]}]
54-
(let [first-message-received* (atom false)
54+
(let [first-response-received* (atom false)
55+
emit-first-message-fn (fn [& args]
56+
(when-not @first-response-received*
57+
(reset! first-response-received* true)
58+
(apply on-first-response-received args)))
5559
on-message-received-wrapper (fn [& args]
56-
(when-not @first-message-received*
57-
(reset! first-message-received* true)
58-
(apply on-first-message-received args))
60+
(apply emit-first-message-fn args)
5961
(apply on-message-received args))
62+
on-prepare-tool-call-wrapper (fn [& args]
63+
(apply emit-first-message-fn args)
64+
(apply on-prepare-tool-call args))
6065
on-error-wrapper (fn [{:keys [exception] :as args}]
6166
(when-not (:silent? (ex-data exception))
6267
(logger/error args)
@@ -78,7 +83,7 @@
7883
:api-key (openai-api-key config)}
7984
{:on-message-received on-message-received-wrapper
8085
:on-error on-error-wrapper
81-
:on-prepare-tool-call on-prepare-tool-call
86+
:on-prepare-tool-call on-prepare-tool-call-wrapper
8287
:on-tool-called on-tool-called
8388
:on-reason on-reason})
8489

@@ -95,8 +100,9 @@
95100
:api-key (anthropic-api-key config)}
96101
{:on-message-received on-message-received-wrapper
97102
:on-error on-error-wrapper
98-
:on-prepare-tool-call on-prepare-tool-call
99-
:on-tool-called on-tool-called})
103+
:on-prepare-tool-call on-prepare-tool-call-wrapper
104+
:on-tool-called on-tool-called
105+
:on-reason on-reason})
100106

101107
(string/starts-with? model config/ollama-model-prefix)
102108
(llm-providers.ollama/completion!
@@ -109,8 +115,9 @@
109115
:user-prompt user-prompt}
110116
{:on-message-received on-message-received-wrapper
111117
:on-error on-error-wrapper
112-
:on-prepare-tool-call on-prepare-tool-call
113-
:on-tool-called on-tool-called})
118+
:on-prepare-tool-call on-prepare-tool-call-wrapper
119+
:on-tool-called on-tool-called
120+
:on-reason on-reason})
114121

115122
:else
116123
(on-error-wrapper {:msg (str "ECA Unsupported model: " model)}))))

src/eca/llm_providers/anthropic.clj

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -129,19 +129,10 @@
129129
(when (= "tool_use" (:type content-block))
130130
(let [function-name (:name content-block)
131131
function-args (:input-json content-block)
132-
{:keys [result past-messages]} (on-tool-called {:id (:id content-block)
133-
:name function-name
134-
:arguments (json/parse-string function-args)})
135-
messages (-> (concat (past-messages->messages past-messages)
136-
[{:role "assistant"
137-
:content [(dissoc content-block :input-json)]}]
138-
(mapv
139-
(fn [{:keys [_type content]}]
140-
{:role "user"
141-
:content [{:type "tool_result"
142-
:tool_use_id (:id content-block)
143-
:content content}]})
144-
(:contents result)))
132+
{:keys [past-messages]} (on-tool-called {:id (:id content-block)
133+
:name function-name
134+
:arguments (json/parse-string function-args)})
135+
messages (-> (past-messages->messages past-messages)
145136
add-cache-to-last-message)]
146137
(base-request!
147138
{:rid (llm-util/gen-rid)

src/eca/llm_providers/ollama.clj

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,12 @@
101101

102102
done_reason
103103
(if-let [tool-call (get @tool-calls* rid)]
104-
(let [{:keys [result past-messages]} (on-tool-called tool-call)]
104+
(let [{:keys [past-messages]} (on-tool-called tool-call)]
105105
(swap! tool-calls* dissoc rid)
106106
(base-completion-request!
107107
{:rid (llm-util/gen-rid)
108108
:url url
109-
:body (assoc body :messages (concat (past-messages->messages past-messages context)
110-
[message]
111-
(mapv
112-
(fn [{:keys [_type content]}]
113-
{:role "tool"
114-
:content content})
115-
(:contents result))))
109+
:body (assoc body :messages (past-messages->messages past-messages context))
116110
:on-error on-error
117111
:on-response handle-response}))
118112
(on-message-received {:type :finish

src/eca/llm_providers/openai.clj

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -91,23 +91,13 @@
9191
(case (:type (:item data))
9292
"function_call" (let [function-name (-> data :item :name)
9393
function-args (-> data :item :arguments)
94-
{:keys [result past-messages]} (on-tool-called {:id (-> data :item :call_id)
95-
:name function-name
96-
:arguments (json/parse-string function-args)})]
94+
{:keys [past-messages]} (on-tool-called {:id (-> data :item :call_id)
95+
:name function-name
96+
:arguments (json/parse-string function-args)})
97+
input (past-messages->input past-messages)]
9798
(base-completion-request!
9899
{:rid (llm-util/gen-rid)
99-
:body (assoc body :input (concat (past-messages->input past-messages)
100-
[{:type "function_call"
101-
:call_id (-> data :item :call_id)
102-
:name function-name
103-
:arguments function-args}]
104-
(mapv
105-
(fn [{:keys [_type content]}]
106-
;; TODO handle different types
107-
{:type "function_call_output"
108-
:call_id (-> data :item :call_id)
109-
:output content})
110-
(:contents result))))
100+
:body (assoc body :input input)
111101
:api-key api-key
112102
:on-error on-error
113103
:on-response handle-response})

test/eca/features/chat_test.clj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
{:message "Hey!"
2929
:request-id "1"}
3030
{:api-mock
31-
(fn [{:keys [on-first-message-received
31+
(fn [{:keys [on-first-response-received
3232
on-message-received]}]
33-
(on-first-message-received {:type :text :text "Hey"})
33+
(on-first-response-received {:type :text :text "Hey"})
3434
(on-message-received {:type :text :text "Hey"})
3535
(on-message-received {:type :text :text " you!"})
3636
(on-message-received {:type :finish}))})]
@@ -106,9 +106,9 @@
106106
{:message "Count with me: 1 mississippi"
107107
:request-id "1"}
108108
{:api-mock
109-
(fn [{:keys [on-first-message-received
109+
(fn [{:keys [on-first-response-received
110110
on-message-received]}]
111-
(on-first-message-received {:type :text :text "2"})
111+
(on-first-response-received {:type :text :text "2"})
112112
(on-message-received {:type :text :text "2"})
113113
(on-message-received {:type :text :text " mississippi"})
114114
(on-message-received {:type :finish}))})
@@ -153,9 +153,9 @@
153153
:chat-id chat-id-1
154154
:request-id "2"}
155155
{:api-mock
156-
(fn [{:keys [on-first-message-received
156+
(fn [{:keys [on-first-response-received
157157
on-message-received]}]
158-
(on-first-message-received {:type :text :text "4"})
158+
(on-first-response-received {:type :text :text "4"})
159159
(on-message-received {:type :text :text "4"})
160160
(on-message-received {:type :text :text " mississippi"})
161161
(on-message-received {:type :finish}))})
@@ -205,11 +205,11 @@ for allowed directories and then list files"
205205
{:message "List the files you are allowed to see"
206206
:request-id "1"}
207207
{:api-mock
208-
(fn [{:keys [on-first-message-received
208+
(fn [{:keys [on-first-response-received
209209
on-message-received
210210
on-prepare-tool-call
211211
on-tool-called]}]
212-
(on-first-message-received {:type :text :text "Ok,"})
212+
(on-first-response-received {:type :text :text "Ok,"})
213213
(on-message-received {:type :text :text "Ok,"})
214214
(on-message-received {:type :text :text " working on it"})
215215
(on-prepare-tool-call {:id "call-1" :name "list_allowed_directories" :argumentsText ""})

0 commit comments

Comments
 (0)