Created
March 27, 2024 00:56
-
-
Save pdenno/c77cf10b5dc7d62c0e3eb157c189dda1 to your computer and use it in GitHub Desktop.
assistant stuff from llm.clj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;;------------------------------------- assistants ---------------------------------------------------- | |
(s/def ::name string?) | |
(s/def ::instructions string?) | |
(s/def ::assistant-args (s/keys :req-un [::name ::instructions])) | |
(defn make-assistant | |
"Create an assistant with the given parameters. Provide a map like with following keys: | |
:name - a string, no default. | |
:instructions - a string, the systems instructions; defaults to 'You are a helpful assistant.', | |
:model - a string; defaults to 'gpt-4-1106-preview', | |
:tools - a vector containing maps; defaults to [{:type 'code_interpreter'}]." | |
[& {:keys [name model-class instructions tools metadata] | |
:or {model-class :gpt-4 | |
metadata {} | |
tools [{:type "code_interpreter"}]} :as obj}] | |
(s/valid? ::assistant-args obj) | |
(let [key (get-api-key :llm)] | |
(openai/create-assistant {:name name | |
:model (pick-llm model-class) | |
:metadata metadata | |
:instructions instructions | |
:tools tools} ; Will be good for csv and xslx, at least. | |
{:api-key key}))) | |
(defn make-thread | |
[& {:keys [assistant-id metadata] :or {metadata {}}}] | |
(let [key (get-api-key :llm)] | |
(openai/create-thread {:assistant_id assistant-id | |
:metadata metadata} | |
{:api-key key}))) | |
(defn get-thread | |
"Get the thread object of the argument PID." | |
[pid] | |
(let [eid (db/project-exists? pid)] | |
(-> (resolve-db-id {:db/id eid} | |
(connect-atm pid) | |
:keep-set #{:project/surrogate :surrogate/thread-str}) | |
:project/surrogate | |
:surrogate/thread-str | |
edn/read-string))) | |
(defn get-assistant | |
"Get the thread object of the argument PID." | |
[pid] | |
(let [eid (db/project-exists? pid)] | |
(-> (resolve-db-id {:db/id eid} | |
(connect-atm pid) | |
:keep-set #{:project/surrogate :surrogate/assistant-obj-str}) | |
:project/surrogate | |
:surrogate/assistant-obj-str | |
edn/read-string))) | |
;;; This is entirely because the OpenAI objects have stuff I find distracting! | |
(def keep-prop? #{:role :content :created_at}) | |
(defn message-salient | |
"Return interesting parts of messages from the structure returned from openai/list-messages." | |
[msgs] | |
(let [_has-more? (:has_more msgs)] ; ToDo: later. | |
(->> msgs | |
:data | |
(mapv (fn [msg] (reduce-kv (fn [m k v] (if (keep-prop? k) (assoc m k v) m)) {} msg))) | |
(sort-by :created) | |
vec))) | |
(defn query-on-thread | |
"Create a message for ROLE on the project's (PID) thread and run it, returning the result text. | |
pid - The project's ID (keyword), | |
role - #{'user' 'assistant'}, | |
msg-text - a string." | |
[pid role msg-text & {:keys [timeout-secs] :or {timeout-secs 40}}] | |
(assert (keyword? pid)) | |
(assert (#{"user" "assistant"} role)) | |
(assert (string? msg-text)) | |
(let [key (get-api-key :llm) | |
aid (-> pid get-assistant :id) | |
tid (-> pid get-thread :id) | |
_msg (openai/create-message ; Apparently the thread_id links the run to msg. | |
{:thread_id tid | |
:role role | |
:content msg-text} | |
{:api-key key}) | |
;; https://platform.openai.com/docs/assistants/overview?context=without-streaming | |
;; Once all the user Messages have been added to the Thread, you can Run the Thread with any Assistant. | |
run (openai/create-run | |
{:thread_id tid | |
:assistant_id aid} | |
{:api-key key})] | |
(loop [secs 0] | |
(let [r (openai/retrieve-run {:thread_id tid :run-id (:id run)} {:api-key key})] | |
(Thread/sleep 1000) | |
(cond (> secs timeout-secs) (throw (ex-info "query-on-thread: Timeout:" {:msg-text msg-text})), | |
(= "completed" (:status r)) (let [[m1 m2] (-> (openai/list-messages {:thread_id tid :limit 2} {:api-key key}) | |
message-salient)] | |
(if (= msg-text (-> m2 :content first :text :value)) | |
(-> m1 :content first :text :value) | |
(throw (ex-info "query-on-thread: Response not synced to query:" {:m1 m1 :m2 m2})))), | |
(#{"expired" "failed"} (:status r)) (throw (ex-info "query-on-thread failed:" {:status (:status r)})) | |
:else (recur (inc secs))))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment