diff --git a/src/ie/simm/db.clj b/src/ie/simm/db.clj index 905b490..f401925 100644 --- a/src/ie/simm/db.clj +++ b/src/ie/simm/db.clj @@ -1 +1,90 @@ -(ns ie.simm.db) \ No newline at end of file +(ns ie.simm.db + (:require [clojure.java.io :as io] + [datahike.api :as d] + [clojure.string :as str])) + +(def default-schema (read-string (slurp "resources/default_schema.edn"))) + +(defn ensure-conn [peer chat-id] + (or (get-in @peer [:conn chat-id]) + (let [;; ensure "databases directory exists and creating it if not" + path (str "databases/" chat-id) + _ (io/make-parents path) + cfg {:store {:backend :file :scope "simm.ie/chats" :path path}} + conn + (try + (let [cfg (d/create-database cfg) + conn (d/connect cfg)] + (d/transact conn default-schema) + conn) + (catch Exception _ + (d/connect cfg)))] + #_(d/transact conn default-schema) + (swap! peer assoc-in [:conn chat-id] conn) + conn))) + +(defn conversation [db chat-id window-size] + (->> + (d/q '[:find ?d ?f ?l ?n ?t + :in $ ?chat + :where + [?c :chat/id ?chat] + [?e :message/chat ?c] + [?e :message/text ?t] + [?e :message/date ?d] + [?e :message/from ?u] + [(get-else $ ?u :from/username "") ?n] + [(get-else $ ?u :from/first_name "") ?f] + [(get-else $ ?u :from/last_name "") ?l]] + db chat-id) + (sort-by first) + (take-last window-size) + (map (fn [[d f l n t]] (str d " " f " " l " (" n "): " (str/replace t #"\n" " ")))) + (str/join "\n"))) + +(defn extract-tags [text] + (vec (distinct (map second (re-seq #"\[\[([^\[\]]+)\](\[.+\])?\]" text))))) + +(defn msg->txs [message] + (let [{:keys [message_id from chat date text]} message + tags (when text (extract-tags text))] + (vec + (concat + (when from + [(let [{:keys [id is_bot first_name last_name username language_code]} from] + (merge + {:from/id (long id) + :from/is_bot is_bot} + (when username + {:from/username username}) + (when language_code + {:from/language_code language_code}) + (when first_name + {:from/first_name first_name}) + (when last_name + {:from/last_name last_name})))]) + (when chat + [(let [{:keys [id first_name username type title all_members_are_administrators]} chat] + (merge + {:chat/id (long id) + :chat/type type} + (when username + {:chat/username username}) + (when first_name + {:chat/first_name first_name}) + (when title + {:chat/title title}) + (when all_members_are_administrators + {:chat/all_members_are_administrators all_members_are_administrators})))]) + [(merge + {:message/id (long message_id) + :message/from [:from/id (long (:id from))] + :message/chat [:chat/id (long (:id chat))] + :message/date (java.util.Date. (long (* 1000 date)))} + (when text + {:message/text text}) + (when (seq tags) + {:message/tag tags}))])))) + +(def window-size 10) + diff --git a/src/ie/simm/runtimes/relational_assistance.clj b/src/ie/simm/runtimes/assistance.clj similarity index 89% rename from src/ie/simm/runtimes/relational_assistance.clj rename to src/ie/simm/runtimes/assistance.clj index 0ce5754..e9969e3 100644 --- a/src/ie/simm/runtimes/relational_assistance.clj +++ b/src/ie/simm/runtimes/assistance.clj @@ -1,4 +1,4 @@ -(ns ie.simm.runtimes.relational-assistance +(ns ie.simm.runtimes.assistance "Active conversation mode that tries to extract factual and procedural knowledge from the user for later use in reasoning and recall. Properties: stateful, persistent, durable" @@ -8,6 +8,7 @@ [ie.simm.languages.browser :refer [extract-body]] [ie.simm.languages.chat :refer [send-text! send-photo! send-document!]] [ie.simm.prompts :as pr] + [ie.simm.db :refer [ensure-conn conversation extract-tags msg->txs window-size]] [superv.async :refer [? put? go-for] :as sasync] [clojure.core.async :refer [chan pub sub mult tap timeout] :as async] [taoensso.timbre :refer [debug info warn error]] @@ -18,91 +19,6 @@ [etaoin.api :as e]) (:import [java.util.zip ZipEntry ZipOutputStream])) -(def default-schema (read-string (slurp "resources/default_schema.edn"))) - -(defn ensure-conn [peer chat-id] - (or (get-in @peer [:conn chat-id]) - (let [;; ensure "databases directory exists and creating it if not" - path (str "databases/" chat-id) - _ (io/make-parents path) - cfg {:store {:backend :file :scope "simm.ie/chats" :path path}} - conn - (try - (let [cfg (d/create-database cfg) - conn (d/connect cfg)] - (d/transact conn default-schema) - conn) - (catch Exception _ - (d/connect cfg)))] - #_(d/transact conn default-schema) - (swap! peer assoc-in [:conn chat-id] conn) - conn))) - -(defn conversation [db chat-id window-size] - (->> - (d/q '[:find ?d ?f ?l ?n ?t - :in $ ?chat - :where - [?c :chat/id ?chat] - [?e :message/chat ?c] - [?e :message/text ?t] - [?e :message/date ?d] - [?e :message/from ?u] - [(get-else $ ?u :from/username "") ?n] - [(get-else $ ?u :from/first_name "") ?f] - [(get-else $ ?u :from/last_name "") ?l]] - db chat-id) - (sort-by first) - (take-last window-size) - (map (fn [[d f l n t]] (str d " " f " " l " (" n "): " (str/replace t #"\n" " ")))) - (str/join "\n"))) - -(defn extract-tags [text] - (vec (distinct (map second (re-seq #"\[\[([^\[\]]+)\](\[.+\])?\]" text))))) - -(defn msg->txs [message] - (let [{:keys [message_id from chat date text]} message - tags (when text (extract-tags text))] - (vec - (concat - (when from - [(let [{:keys [id is_bot first_name last_name username language_code]} from] - (merge - {:from/id (long id) - :from/is_bot is_bot} - (when username - {:from/username username}) - (when language_code - {:from/language_code language_code}) - (when first_name - {:from/first_name first_name}) - (when last_name - {:from/last_name last_name})))]) - (when chat - [(let [{:keys [id first_name username type title all_members_are_administrators]} chat] - (merge - {:chat/id (long id) - :chat/type type} - (when username - {:chat/username username}) - (when first_name - {:chat/first_name first_name}) - (when title - {:chat/title title}) - (when all_members_are_administrators - {:chat/all_members_are_administrators all_members_are_administrators})))]) - [(merge - {:message/id (long message_id) - :message/from [:from/id (long (:id from))] - :message/chat [:chat/id (long (:id chat))] - :message/date (java.util.Date. (long (* 1000 date)))} - (when text - {:message/text text}) - (when (seq tags) - {:message/tag tags}))])))) - -(def window-size 10) - (defn summarize [S conn conv chat] (go-try S (debug "=========================== SUMMARIZING ===============================") @@ -175,7 +91,7 @@ (.close zip-out) zip-file)) -(defn relational-assistance +(defn assistance "This interpreter can derive facts and effects through a relational database." [[S peer [in out]]] ;; pass everything from in to next-in for the next middleware diff --git a/src/ie/simm/runtimes/notes.clj b/src/ie/simm/runtimes/notes.clj index 8e1ceba..3474a4f 100644 --- a/src/ie/simm/runtimes/notes.clj +++ b/src/ie/simm/runtimes/notes.clj @@ -1,22 +1,241 @@ (ns ie.simm.runtimes.notes - "Note taking runtime. + "Active conversation mode that tries to extract factual and procedural knowledge from the user for later use in reasoning and recall. Properties: stateful, persistent, durable" (:require [ie.simm.languages.bindings :as lb] [ie.simm.languages.gen-ai :refer [cheap-llm reasoner-llm stt-basic image-gen]] + [ie.simm.languages.web-search :refer [search]] + [ie.simm.languages.browser :refer [extract-body]] + [ie.simm.languages.chat :refer [send-text! send-photo! send-document!]] [ie.simm.prompts :as pr] - [superv.async :refer [? put?]] - [clojure.core.async :refer [chan pub sub mult tap timeout]] + [ie.simm.db :refer [ensure-conn conversation extract-tags msg->txs window-size]] + [superv.async :refer [? put? go-for] :as sasync] + [clojure.core.async :refer [chan pub sub mult tap timeout] :as async] [taoensso.timbre :refer [debug info warn error]] [datahike.api :as d] [hasch.core :refer [uuid]] [clojure.string :as str] - [clojure.java.io :as io])) + [clojure.java.io :as io] + [etaoin.api :as e]) + (:import [java.util.zip ZipEntry ZipOutputStream])) -(defn summarize [conv] +(defn summarize [S conn conv chat] (go-try S - (let [summarization (> (d/q '[:find ?d ?e + :in $ ?chat + :where + [?c :chat/id ?chat] + [?e :message/chat ?c] + [?e :message/date ?d]] + db (:id chat)) + (sort-by first) + (take-last window-size) + (map second)) + note-titles (extract-tags summarization) + _ (debug "=========================== CREATING NOTES ===============================") + new-notes + (txs msg)) + _ (debug "transacted") + ;; 2. recent conversation + conv (conversation @conn (:id chat) window-size) + _ (debug "conversation" conv) + _ (debug "message count" (d/q '[:find (count ?m) . + :in $ ?cid + :where + [?m :message/chat ?c] + [?c :chat/id ?cid]] + @conn (:id chat))) + _ (when (<= (mod (d/q '[:find (count ?m) . + :in $ ?cid + :where + [?m :message/chat ?c] + [?c :chat/id ?cid]] + @conn (:id chat)) + window-size) 1) + (summarize S conn conv chat)) + + + ;; 3. retrieve summaries for active tags + all-tags (d/q '[:find [?t ...] :where [_ :conversation/tag ?t]] @conn) + relevant (txs (:result (txs (:result (txs (:result (txs (:result (txs (:result (txs (:result (txs (:result (txs (:result (txs (:result (txs (:result reply-msg)))]))] + ) + (catch Exception e + (let [error-id (uuid)] + (error "Could not process message(" error-id "): " m e) + (