|
201 | 201 | :content {:type :text |
202 | 202 | :text (if (:auto? message-content) |
203 | 203 | "── Chat auto-compacted ──" |
204 | | - "── Chat compacted ──")}}])) |
| 204 | + "── Chat compacted ──")}}] |
| 205 | + "flag" [{:role :system |
| 206 | + :content {:type :flag |
| 207 | + :text (:text message-content) |
| 208 | + :contentId content-id}}])) |
205 | 209 |
|
206 | 210 | (defn ^:private send-chat-contents! [messages chat-ctx] |
207 | 211 | (doseq [message messages] |
|
1169 | 1173 | :db* db* |
1170 | 1174 | :messenger messenger})) |
1171 | 1175 | {})) |
| 1176 | + |
| 1177 | +(defn ^:private find-last-message-idx |
| 1178 | + "Find the last message index matching content-id by checking both |
| 1179 | + :content-id (user messages) and [:content :id] (tool calls, etc)." |
| 1180 | + [messages content-id] |
| 1181 | + (loop [i (dec (count messages))] |
| 1182 | + (cond |
| 1183 | + (neg? i) nil |
| 1184 | + (let [msg (messages i)] |
| 1185 | + (or (= content-id (:content-id msg)) |
| 1186 | + (= content-id (get-in msg [:content :id])))) i |
| 1187 | + :else (recur (dec i))))) |
| 1188 | + |
| 1189 | +(defn add-flag |
| 1190 | + "Add a named flag after the message identified by content-id. |
| 1191 | + Searches both :content-id and [:content :id] to support placement |
| 1192 | + after any message type (user, tool call, reason, etc). |
| 1193 | + Clears and replays the chat to render the flag at the correct position." |
| 1194 | + [{:keys [chat-id content-id text]} db* messenger metrics] |
| 1195 | + (let [messages (vec (get-in @db* [:chats chat-id :messages])) |
| 1196 | + insert-idx (find-last-message-idx messages content-id)] |
| 1197 | + (when insert-idx |
| 1198 | + (let [flag-id (str (random-uuid)) |
| 1199 | + flag-msg {:role "flag" :content {:text text} :content-id flag-id} |
| 1200 | + insert-after (inc insert-idx) |
| 1201 | + new-messages (into (subvec messages 0 insert-after) |
| 1202 | + (cons flag-msg (subvec messages insert-after)))] |
| 1203 | + (swap! db* assoc-in [:chats chat-id :messages] new-messages) |
| 1204 | + (db/update-workspaces-cache! @db* metrics) |
| 1205 | + (messenger/chat-cleared messenger {:chat-id chat-id :messages true}) |
| 1206 | + (send-chat-contents! new-messages {:chat-id chat-id :db* db* :messenger messenger}))) |
| 1207 | + {})) |
| 1208 | + |
| 1209 | +(defn remove-flag |
| 1210 | + "Remove a flag message identified by content-id from the chat." |
| 1211 | + [{:keys [chat-id content-id]} db* metrics] |
| 1212 | + (when-let [messages (get-in @db* [:chats chat-id :messages])] |
| 1213 | + (let [new-messages (vec (remove #(and (= "flag" (:role %)) |
| 1214 | + (= content-id (:content-id %))) |
| 1215 | + messages))] |
| 1216 | + (when (not= (count new-messages) (count messages)) |
| 1217 | + (swap! db* assoc-in [:chats chat-id :messages] new-messages) |
| 1218 | + (db/update-workspaces-cache! @db* metrics)))) |
| 1219 | + {}) |
| 1220 | + |
| 1221 | +(defn fork-chat |
| 1222 | + "Fork the chat creating a new chat with messages up to and including |
| 1223 | + the message identified by content-id." |
| 1224 | + [{:keys [chat-id content-id]} db* messenger metrics] |
| 1225 | + (let [chat (get-in @db* [:chats chat-id]) |
| 1226 | + messages (vec (:messages chat)) |
| 1227 | + target-idx (find-last-message-idx messages content-id)] |
| 1228 | + (when target-idx |
| 1229 | + (let [new-id (str (random-uuid)) |
| 1230 | + now (System/currentTimeMillis) |
| 1231 | + new-title (f.commands/fork-title (:title chat)) |
| 1232 | + kept-messages (subvec messages 0 (inc target-idx)) |
| 1233 | + new-chat {:id new-id |
| 1234 | + :title new-title |
| 1235 | + :status :idle |
| 1236 | + :created-at now |
| 1237 | + :updated-at now |
| 1238 | + :model (:model chat) |
| 1239 | + :last-api (:last-api chat) |
| 1240 | + :messages kept-messages |
| 1241 | + :prompt-finished? true}] |
| 1242 | + (swap! db* assoc-in [:chats new-id] new-chat) |
| 1243 | + (db/update-workspaces-cache! @db* metrics) |
| 1244 | + (messenger/chat-opened messenger {:chat-id new-id :title new-title}) |
| 1245 | + (send-chat-contents! kept-messages {:chat-id new-id :db* db* :messenger messenger}) |
| 1246 | + (lifecycle/send-content! {:messenger messenger :chat-id new-id} |
| 1247 | + :system |
| 1248 | + (assoc-some {:type :metadata} :title new-title)) |
| 1249 | + (lifecycle/send-content! {:messenger messenger :chat-id chat-id} |
| 1250 | + :system |
| 1251 | + {:type :text :text (str "Chat forked to: " new-title)}))) |
| 1252 | + {})) |
0 commit comments