|
3 | 3 | [clojure.test :refer [deftest is testing]] |
4 | 4 | [integration.eca :as eca] |
5 | 5 | [integration.fixture :as fixture] |
| 6 | + [integration.helper :refer [match-content] :as h] |
6 | 7 | [llm-mock.mocks :as llm.mocks] |
7 | 8 | [matcher-combinators.matchers :as m] |
8 | 9 | [matcher-combinators.test :refer [match?]])) |
9 | 10 |
|
10 | 11 | (eca/clean-after-test) |
11 | 12 |
|
12 | | -(defn match-content [chat-id request-id role content] |
13 | | - (is (match? |
14 | | - {:chatId chat-id |
15 | | - :requestId request-id |
16 | | - :role role |
17 | | - :content content} |
18 | | - (eca/client-awaits-server-notification :chat/contentReceived)))) |
19 | | - |
20 | 13 | (deftest simple-text |
21 | 14 | (eca/start-process!) |
22 | 15 |
|
|
210 | 203 | {:role "user" :content [{:type "input_text" :text "how are you?"}]}] |
211 | 204 | :instructions (m/pred string?)} |
212 | 205 | llm.mocks/*last-req-body*)))))) |
| 206 | + |
| 207 | +(deftest tool-calling |
| 208 | + (eca/start-process!) |
| 209 | + |
| 210 | + (eca/request! (fixture/initialize-request)) |
| 211 | + (eca/notify! (fixture/initialized-notification)) |
| 212 | + (let [chat-id* (atom nil)] |
| 213 | + (testing "We ask what files LLM see" |
| 214 | + (llm.mocks/set-case! :tool-calling-0) |
| 215 | + (let [req-id 0 |
| 216 | + resp (eca/request! (fixture/chat-prompt-request |
| 217 | + {:request-id req-id |
| 218 | + :model "gpt-5" |
| 219 | + :message "What files you see?"})) |
| 220 | + chat-id (reset! chat-id* (:chatId resp))] |
| 221 | + |
| 222 | + (is (match? |
| 223 | + {:chatId (m/pred string?) |
| 224 | + :model "gpt-5" |
| 225 | + :status "success"} |
| 226 | + resp)) |
| 227 | + |
| 228 | + (match-content chat-id req-id "user" {:type "text" :text "What files you see?\n"}) |
| 229 | + (match-content chat-id req-id "system" {:type "progress" :state "running" :text "Waiting model"}) |
| 230 | + (match-content chat-id req-id "system" {:type "progress" :state "running" :text "Generating"}) |
| 231 | + (match-content chat-id req-id "assistant" {:type "reasonStarted" :id "123"}) |
| 232 | + (match-content chat-id req-id "assistant" {:type "reasonText" :id "123" :text "I should call tool"}) |
| 233 | + (match-content chat-id req-id "assistant" {:type "reasonText" :id "123" :text " eca_directory_tree"}) |
| 234 | + (match-content chat-id req-id "assistant" {:type "reasonFinished" :id "123"}) |
| 235 | + (match-content chat-id req-id "assistant" {:type "text" :text "I will list files"}) |
| 236 | + (match-content chat-id req-id "assistant" {:type "toolCallPrepare" |
| 237 | + :origin "native" |
| 238 | + :id "tool-1" |
| 239 | + :name "eca_directory_tree" |
| 240 | + :argumentsText "" |
| 241 | + :manualApproval false |
| 242 | + :summary "Listing file tree"}) |
| 243 | + (match-content chat-id req-id "assistant" {:type "toolCallPrepare" |
| 244 | + :origin "native" |
| 245 | + :id "tool-1" |
| 246 | + :name "eca_directory_tree" |
| 247 | + :argumentsText "{\"pat" |
| 248 | + :manualApproval false |
| 249 | + :summary "Listing file tree"}) |
| 250 | + (match-content chat-id req-id "assistant" {:type "toolCallPrepare" |
| 251 | + :origin "native" |
| 252 | + :id "tool-1" |
| 253 | + :name "eca_directory_tree" |
| 254 | + :argumentsText (str "{\"path\":\"" (h/project-path->canon-path "resources") "\"}") |
| 255 | + :manualApproval false |
| 256 | + :summary "Listing file tree"}) |
| 257 | + (match-content chat-id req-id "system" {:type "usage" |
| 258 | + :messageInputTokens 5 |
| 259 | + :messageOutputTokens 30 |
| 260 | + :sessionTokens 35 |
| 261 | + :messageCost (m/pred string?) |
| 262 | + :sessionCost (m/pred string?)}) |
| 263 | + (match-content chat-id req-id "assistant" {:type "toolCallRun" |
| 264 | + :origin "native" |
| 265 | + :id "tool-1" |
| 266 | + :name "eca_directory_tree" |
| 267 | + :arguments {:path (h/project-path->canon-path "resources")} |
| 268 | + :manualApproval false |
| 269 | + :summary "Listing file tree"}) |
| 270 | + (match-content chat-id req-id "assistant" {:type "toolCalled" |
| 271 | + :origin "native" |
| 272 | + :id "tool-1" |
| 273 | + :name "eca_directory_tree" |
| 274 | + :arguments {:path (h/project-path->canon-path "resources")} |
| 275 | + :summary "Listing file tree" |
| 276 | + :error false |
| 277 | + :outputs [{:type "text" :text (str "[FILE] " (h/project-path->canon-path "resources/file2.md\n") |
| 278 | + "[FILE] " (h/project-path->canon-path "resources/file1.md\n"))}]}) |
| 279 | + (match-content chat-id req-id "assistant" {:type "text" :text "The files I see:\n"}) |
| 280 | + (match-content chat-id req-id "assistant" {:type "text" :text "file1\nfile2\n"}) |
| 281 | + (match-content chat-id req-id "system" {:type "usage" |
| 282 | + :messageInputTokens 5 |
| 283 | + :messageOutputTokens 30 |
| 284 | + :sessionTokens 70 |
| 285 | + :messageCost (m/pred string?) |
| 286 | + :sessionCost (m/pred string?)}) |
| 287 | + (match-content chat-id req-id "system" {:type "progress" :state "finished"}) |
| 288 | + (is (match? |
| 289 | + {:input [{:role "user" :content [{:type "input_text" :text "What files you see?"}]} |
| 290 | + {:type "reasoning" |
| 291 | + :id "123" |
| 292 | + :summary [{:type "summary_text" :text "I should call tool eca_directory_tree"}] |
| 293 | + :encrypted_content "enc-123"} |
| 294 | + {:role "assistant" :content [{:type "output_text" :text "I will list files"}]} |
| 295 | + {:type "function_call" |
| 296 | + :name "eca_directory_tree" |
| 297 | + :call_id "tool-1" |
| 298 | + :arguments (str "{\"path\":\"" (h/project-path->canon-path "resources") "\"}")} |
| 299 | + {:type "function_call_output" |
| 300 | + :call_id "tool-1" |
| 301 | + :output (str "[FILE] " (h/project-path->canon-path "resources/file2.md\n") |
| 302 | + "[FILE] " (h/project-path->canon-path "resources/file1.md\n\n"))}] |
| 303 | + :tools (m/embeds |
| 304 | + [{:name "eca_directory_tree"}]) |
| 305 | + :instructions (m/pred string?)} |
| 306 | + llm.mocks/*last-req-body*)))))) |
0 commit comments