|
1 | 1 | (ns eca.features.chat |
2 | 2 | (:require |
3 | 3 | [cheshire.core :as json] |
| 4 | + [clojure.java.io :as io] |
4 | 5 | [clojure.set :as set] |
5 | 6 | [clojure.string :as string] |
6 | 7 | [eca.config :as config] |
|
721 | 722 | total-time-ms (- (System/currentTimeMillis) start-time)] |
722 | 723 | (add-to-history! {:role "tool_call" |
723 | 724 | :content (assoc tool-call |
| 725 | + :name name |
724 | 726 | :details details |
725 | 727 | :summary summary |
726 | 728 | :origin origin |
727 | 729 | :server server-name)}) |
728 | 730 | (add-to-history! {:role "tool_call_output" |
729 | 731 | :content (assoc tool-call |
| 732 | + :name name |
730 | 733 | :error (:error result) |
731 | 734 | :output result |
732 | 735 | :total-time-ms total-time-ms |
|
886 | 889 | :text error-message}) |
887 | 890 | (prompt-messages! messages chat-ctx)))) |
888 | 891 |
|
889 | | -(defn ^:private message-content->chat-content [role message-content] |
| 892 | +(defn ^:private message-content->chat-content [role message-content content-id] |
890 | 893 | (case role |
891 | 894 | ("user" |
892 | 895 | "system" |
893 | | - "assistant") [(reduce |
894 | | - (fn [m content] |
895 | | - (case (:type content) |
896 | | - :text (assoc m |
897 | | - :type :text |
898 | | - :text (str (:text m) "\n" (:text content))) |
899 | | - m)) |
900 | | - {} |
901 | | - message-content)] |
902 | | - "tool_call" [{:type :toolCallPrepare |
903 | | - :origin (:origin message-content) |
904 | | - :name (:name message-content) |
905 | | - :server (:server message-content) |
906 | | - :arguments-text "" |
907 | | - :id (:id message-content)}] |
908 | | - "tool_call_output" [{:type :toolCalled |
909 | | - :origin (:origin message-content) |
910 | | - :name (:name message-content) |
911 | | - :server (:server message-content) |
912 | | - :arguments (:arguments message-content) |
913 | | - :total-time-ms (:total-time-ms message-content) |
914 | | - :error (:error message-content) |
| 896 | + "assistant") [{:role role |
| 897 | + :content (reduce |
| 898 | + (fn [m content] |
| 899 | + (case (:type content) |
| 900 | + :text (assoc m |
| 901 | + :type :text |
| 902 | + :text (str (:text m) "\n" (:text content))) |
| 903 | + m)) |
| 904 | + (assoc-some {} :content-id content-id) |
| 905 | + message-content)}] |
| 906 | + "tool_call" [{:role :assistant |
| 907 | + :content {:type :toolCallPrepare |
| 908 | + :origin (:origin message-content) |
| 909 | + :name (:name message-content) |
| 910 | + :server (:server message-content) |
| 911 | + :summary (:summary message-content) |
| 912 | + :details (:details message-content) |
| 913 | + :arguments-text "" |
| 914 | + :id (:id message-content)}}] |
| 915 | + "tool_call_output" [{:role :assistant |
| 916 | + :content {:type :toolCalled |
| 917 | + :origin (:origin message-content) |
| 918 | + :name (:name message-content) |
| 919 | + :server (:server message-content) |
| 920 | + :arguments (:arguments message-content) |
| 921 | + :total-time-ms (:total-time-ms message-content) |
| 922 | + :summary (:summary message-content) |
| 923 | + :details (:details message-content) |
| 924 | + :error (:error message-content) |
| 925 | + :id (:id message-content) |
| 926 | + :outputs (:contents (:output message-content))}}] |
| 927 | + "reason" [{:role :assistant |
| 928 | + :content {:type :reasonStarted |
| 929 | + :id (:id message-content)}} |
| 930 | + {:role :assistant |
| 931 | + :content {:type :reasonText |
915 | 932 | :id (:id message-content) |
916 | | - :outputs (:contents (:output message-content))}] |
917 | | - "reason" [{:type :reasonStarted |
918 | | - :id (:id message-content)} |
919 | | - {:type :reasonText |
920 | | - :id (:id message-content) |
921 | | - :text (:text message-content)} |
922 | | - {:type :reasonFinished |
923 | | - :id (:id message-content) |
924 | | - :total-time-ms (:total-time-ms message-content)}])) |
| 933 | + :text (:text message-content)}} |
| 934 | + {:role :assistant |
| 935 | + :content {:type :reasonFinished |
| 936 | + :id (:id message-content) |
| 937 | + :total-time-ms (:total-time-ms message-content)}}])) |
925 | 938 |
|
926 | 939 | (defn ^:private send-chat-contents! [messages chat-ctx] |
927 | 940 | (doseq [message messages] |
928 | | - (doseq [chat-content (message-content->chat-content (:role message) (:content message))] |
| 941 | + (doseq [{:keys [role content]} (message-content->chat-content (:role message) (:content message) (:content-id message))] |
929 | 942 | (send-content! chat-ctx |
930 | | - (:role message) |
931 | | - chat-content)))) |
| 943 | + role |
| 944 | + content)))) |
932 | 945 |
|
933 | 946 | (defn ^:private handle-command! [{:keys [command args]} chat-ctx] |
934 | 947 | (let [{:keys [type on-finished-side-effect] :as result} (f.commands/handle-command! command args chat-ctx)] |
|
1115 | 1128 | Then notify to clear chat and then the kept messages." |
1116 | 1129 | [{:keys [chat-id content-id]} db* messenger] |
1117 | 1130 | (let [all-messages (get-in @db* [:chats chat-id :messages]) |
1118 | | - new-messages (vec (take-while #(not= (:content-id %) content-id) all-messages))] |
| 1131 | + tool-calls (get-in @db* [:chats chat-id :tool-calls]) |
| 1132 | + new-messages (vec (take-while #(not= (:content-id %) content-id) all-messages)) |
| 1133 | + removed-messages (vec (drop-while #(not= (:content-id %) content-id) all-messages)) |
| 1134 | + rollback-changes (->> removed-messages |
| 1135 | + (filter #(= "tool_call_output" (:role %))) |
| 1136 | + (keep #(get-in tool-calls [(:id (:content %)) :rollback-changes])) |
| 1137 | + flatten |
| 1138 | + reverse)] |
| 1139 | + (doseq [{:keys [path content]} rollback-changes] |
| 1140 | + (logger/info (format "Rolling back change for '%s' to content: '%s'" path content)) |
| 1141 | + (if content |
| 1142 | + (spit path content) |
| 1143 | + (io/delete-file path true))) |
1119 | 1144 | (swap! db* assoc-in [:chats chat-id :messages] new-messages) |
1120 | 1145 | (messenger/chat-cleared |
1121 | 1146 | messenger |
|
0 commit comments