|
338 | 338 | :on-message-received identity}))) |
339 | 339 | (is (= 1 @attempt*)) |
340 | 340 | (is (true? @on-error-called*))))) |
| 341 | + |
| 342 | +(deftest sync-retry-on-custom-retry-rule-test |
| 343 | + (testing "retries when custom retryRules status matches" |
| 344 | + (let [attempt* (atom 0) |
| 345 | + retry-events* (atom []) |
| 346 | + on-error-called* (atom false)] |
| 347 | + (with-redefs [eca.llm-api/prompt! (fn [_opts] |
| 348 | + (let [attempt (swap! attempt* inc)] |
| 349 | + (if (= 1 attempt) |
| 350 | + {:error {:status 418 |
| 351 | + :body "I'm a teapot" |
| 352 | + :message "LLM response status: 418"}} |
| 353 | + {:output-text "success" |
| 354 | + :usage {:input-tokens 10 :output-tokens 5}}))) |
| 355 | + eca.llm-api/sleep-with-cancel (fn [_ cancelled?] (not (cancelled?)))] |
| 356 | + (llm-api/sync-or-async-prompt! |
| 357 | + (make-prompt-opts |
| 358 | + {:stream false |
| 359 | + :config {:providers {"anthropic" {:key "test-key" |
| 360 | + :url "http://test" |
| 361 | + :retryRules [{:status 418 :label "Proxy throttle"}] |
| 362 | + :models {"claude-sonnet-4-6" {:extraPayload {:stream false}}}}}} |
| 363 | + :on-retry (fn [event] (swap! retry-events* conj event)) |
| 364 | + :on-error (fn [_] (reset! on-error-called* true)) |
| 365 | + :on-message-received identity}))) |
| 366 | + (is (= 2 @attempt*)) |
| 367 | + (is (= 1 (count @retry-events*))) |
| 368 | + (is (= :retryable-custom (get-in (first @retry-events*) [:classified :error/type]))) |
| 369 | + (is (= "Proxy throttle" (get-in (first @retry-events*) [:classified :error/label]))) |
| 370 | + (is (false? @on-error-called*))))) |
| 371 | + |
| 372 | +(deftest async-retry-on-custom-retry-rule-body-pattern-test |
| 373 | + (testing "retries async when custom retryRules bodyPattern matches" |
| 374 | + (let [attempt* (atom 0) |
| 375 | + retry-events* (atom []) |
| 376 | + received-text* (atom "") |
| 377 | + on-error-called* (atom false)] |
| 378 | + (with-redefs [eca.llm-api/prompt! (fn [{:keys [on-message-received on-error]}] |
| 379 | + (let [attempt (swap! attempt* inc)] |
| 380 | + (if (= 1 attempt) |
| 381 | + (on-error {:status 500 |
| 382 | + :body "server capacity exceeded" |
| 383 | + :message "LLM response status: 500"}) |
| 384 | + (do |
| 385 | + (on-message-received {:type :text :text "hello"}) |
| 386 | + (on-message-received {:type :finish :finish-reason "stop"}))))) |
| 387 | + eca.llm-api/sleep-with-cancel (fn [_ cancelled?] (not (cancelled?)))] |
| 388 | + (llm-api/sync-or-async-prompt! |
| 389 | + (make-prompt-opts |
| 390 | + {:config {:providers {"anthropic" {:key "test-key" |
| 391 | + :url "http://test" |
| 392 | + :retryRules [{:bodyPattern "capacity.*exceeded" |
| 393 | + :label "Capacity exceeded"}] |
| 394 | + :models {"claude-sonnet-4-6" {}}}}} |
| 395 | + :on-retry (fn [event] (swap! retry-events* conj event)) |
| 396 | + :on-error (fn [_] (reset! on-error-called* true)) |
| 397 | + :on-message-received (fn [{:keys [type text]}] |
| 398 | + (when (= :text type) |
| 399 | + (swap! received-text* str text)))}))) |
| 400 | + (is (= 2 @attempt*)) |
| 401 | + (is (= 1 (count @retry-events*))) |
| 402 | + (is (= "Capacity exceeded" (get-in (first @retry-events*) [:classified :error/label]))) |
| 403 | + (is (false? @on-error-called*)) |
| 404 | + (is (= "hello" @received-text*)))) |
| 405 | + |
| 406 | + (testing "does not retry when no custom rule matches" |
| 407 | + (let [attempt* (atom 0) |
| 408 | + on-error-called* (atom false)] |
| 409 | + (with-redefs [eca.llm-api/prompt! (fn [{:keys [on-error]}] |
| 410 | + (swap! attempt* inc) |
| 411 | + (on-error {:status 418 |
| 412 | + :body "I'm a teapot" |
| 413 | + :message "LLM response status: 418"})) |
| 414 | + eca.llm-api/sleep-with-cancel (fn [_ _] true)] |
| 415 | + (llm-api/sync-or-async-prompt! |
| 416 | + (make-prompt-opts |
| 417 | + {:config {:providers {"anthropic" {:key "test-key" |
| 418 | + :url "http://test" |
| 419 | + :retryRules [{:status 599 :label "Something else"}] |
| 420 | + :models {"claude-sonnet-4-6" {}}}}} |
| 421 | + :on-error (fn [_] (reset! on-error-called* true)) |
| 422 | + :on-message-received identity}))) |
| 423 | + (is (= 1 @attempt*)) |
| 424 | + (is (true? @on-error-called*))))) |
0 commit comments