|
333 | 333 | {:type :prompt-message |
334 | 334 | :message message}))) |
335 | 335 |
|
| 336 | +(defn ^:private truncated-response? |
| 337 | + "Returns true when the response text shows signs of being truncated mid-stream. |
| 338 | + Checks for unclosed code fences (odd number of ``` markers at line start)." |
| 339 | + [^String text] |
| 340 | + (when-not (string/blank? text) |
| 341 | + (odd? (count (re-seq #"(?m)^```" text))))) |
| 342 | + |
336 | 343 | (defn ^:private trigger-auto-compact! |
337 | 344 | "Trigger auto-compact: send compact prompt, then resume the original task." |
338 | 345 | [{:keys [db* config chat-id agent] :as chat-ctx} |
|
527 | 534 | :text (str "API limit reached. Tokens: " |
528 | 535 | (json/generate-string (:tokens msg)))}) |
529 | 536 | (lifecycle/finish-chat-prompt! :idle (dissoc chat-ctx :on-finished-side-effect))) |
530 | | - :finish (do (add-to-history! {:role "assistant" |
531 | | - :content [{:type :text :text @received-msgs*}]}) |
532 | | - (if (and (:premature? msg) |
533 | | - (not (:auto-continued? chat-ctx)) |
534 | | - (not (:on-finished-side-effect chat-ctx))) |
535 | | - (do |
536 | | - (logger/info logger-tag "Premature stream stop detected, auto-continuing" {:chat-id chat-id}) |
537 | | - (lifecycle/send-content! chat-ctx :system |
538 | | - {:type :text :text "Response was interrupted. Continuing..."}) |
539 | | - (swap! db* assoc-in [:chats chat-id :auto-compacting?] true) |
540 | | - (lifecycle/finish-chat-prompt! :idle |
541 | | - (assoc chat-ctx :on-finished-side-effect |
542 | | - (fn [] |
543 | | - (swap! db* update-in [:chats chat-id] dissoc :auto-compacting?) |
544 | | - (prompt-messages! |
545 | | - [{:role "user" |
546 | | - :content [{:type :text |
547 | | - :text "Your previous response was interrupted mid-stream. Continue from where you left off."}]}] |
548 | | - :auto-continue |
549 | | - (assoc chat-ctx :auto-continued? true)))))) |
550 | | - (lifecycle/finish-chat-prompt! :idle chat-ctx))))) |
| 537 | + :finish (let [response-text @received-msgs*] |
| 538 | + (add-to-history! {:role "assistant" |
| 539 | + :content [{:type :text :text response-text}]}) |
| 540 | + (if (and (or (:premature? msg) |
| 541 | + (truncated-response? response-text)) |
| 542 | + (not (:auto-continued? chat-ctx)) |
| 543 | + (not (:on-finished-side-effect chat-ctx))) |
| 544 | + (do |
| 545 | + (logger/info logger-tag "Truncated or premature response detected, auto-continuing" |
| 546 | + {:chat-id chat-id |
| 547 | + :premature? (:premature? msg) |
| 548 | + :truncated? (truncated-response? response-text)}) |
| 549 | + (lifecycle/send-content! chat-ctx :system |
| 550 | + {:type :text :text "Response was interrupted. Continuing..."}) |
| 551 | + (swap! db* assoc-in [:chats chat-id :auto-compacting?] true) |
| 552 | + (lifecycle/finish-chat-prompt! :idle |
| 553 | + (assoc chat-ctx :on-finished-side-effect |
| 554 | + (fn [] |
| 555 | + (swap! db* update-in [:chats chat-id] dissoc :auto-compacting?) |
| 556 | + (prompt-messages! |
| 557 | + [{:role "user" |
| 558 | + :content [{:type :text |
| 559 | + :text "Your previous response was interrupted mid-stream. Continue from where you left off, do not redo completed steps."}]}] |
| 560 | + :auto-continue |
| 561 | + (assoc chat-ctx :auto-continued? true)))))) |
| 562 | + (lifecycle/finish-chat-prompt! :idle chat-ctx))))) |
551 | 563 | :on-prepare-tool-call (fn [{:keys [id full-name arguments-text]}] |
552 | 564 | (lifecycle/assert-chat-not-stopped! chat-ctx) |
553 | 565 | (let [all-tools (f.tools/all-tools chat-id agent @db* config) |
|
675 | 687 | :text "Context window exceeded. Auto-compacting conversation..."}) |
676 | 688 | (prune-tool-results! db* chat-id {}) |
677 | 689 | (trigger-auto-compact! chat-ctx all-tools user-messages)) |
678 | | - (do |
| 690 | + (let [partial-text @received-msgs* |
| 691 | + transient-error? (contains? #{:overloaded :premature-stop} error-type) |
| 692 | + can-auto-continue? (and (or transient-error? |
| 693 | + (string/includes? (or message "") "idle timeout")) |
| 694 | + (not (:auto-continued? chat-ctx)) |
| 695 | + (not (:on-finished-side-effect chat-ctx)) |
| 696 | + (not compacting?))] |
679 | 697 | (when compacting? |
680 | 698 | (swap! db* update-in [:chats chat-id] dissoc :auto-compacting? :compacting?)) |
681 | | - (lifecycle/send-content! chat-ctx :system {:type :text :text (or message (str "Error: " (or (ex-message exception) (.getName (class exception)))))}) |
682 | | - (lifecycle/finish-chat-prompt! :idle (dissoc chat-ctx :on-finished-side-effect))))))}) |
| 699 | + (when-not (string/blank? partial-text) |
| 700 | + (add-to-history! {:role "assistant" |
| 701 | + :content [{:type :text :text partial-text}]})) |
| 702 | + (if can-auto-continue? |
| 703 | + (do |
| 704 | + (logger/info logger-tag "Transient error during response, auto-continuing" |
| 705 | + {:chat-id chat-id :error-type error-type}) |
| 706 | + (lifecycle/send-content! chat-ctx :system |
| 707 | + {:type :text :text (str (or message "Connection interrupted") ". Continuing...")}) |
| 708 | + (swap! db* assoc-in [:chats chat-id :auto-compacting?] true) |
| 709 | + (lifecycle/finish-chat-prompt! :idle |
| 710 | + (assoc chat-ctx :on-finished-side-effect |
| 711 | + (fn [] |
| 712 | + (swap! db* update-in [:chats chat-id] dissoc :auto-compacting?) |
| 713 | + (prompt-messages! |
| 714 | + [{:role "user" |
| 715 | + :content [{:type :text |
| 716 | + :text "Your previous response was interrupted mid-stream. Continue from where you left off, do not redo completed steps."}]}] |
| 717 | + :auto-continue |
| 718 | + (assoc chat-ctx :auto-continued? true)))))) |
| 719 | + (do |
| 720 | + (lifecycle/send-content! chat-ctx :system {:type :text :text (or message (str "Error: " (or (ex-message exception) (.getName (class exception)))))}) |
| 721 | + (lifecycle/finish-chat-prompt! :idle (dissoc chat-ctx :on-finished-side-effect))))))))}) |
683 | 722 | (catch Exception e |
684 | 723 | (when-not (:silent? (ex-data e)) |
685 | 724 | (logger/error e) |
| 725 | + (when-not (string/blank? @received-msgs*) |
| 726 | + (add-to-history! {:role "assistant" |
| 727 | + :content [{:type :text :text @received-msgs*}]})) |
686 | 728 | (lifecycle/send-content! chat-ctx :system {:type :text :text (str "Error: " (or (ex-message e) (.getName (class e))))}) |
687 | 729 | (lifecycle/finish-chat-prompt! :idle (dissoc chat-ctx :on-finished-side-effect)))) |
688 | 730 | (finally |
|
0 commit comments