|
4 | 4 | [clojure.java.io :as io] |
5 | 5 | [eca.llm-util :as llm-util] |
6 | 6 | [eca.logger :as logger] |
7 | | - [eca.shared :as shared] |
| 7 | + [eca.shared :as shared :refer [assoc-some]] |
8 | 8 | [hato.client :as http])) |
9 | 9 |
|
10 | 10 | (set! *warn-on-reflection* true) |
|
26 | 26 | :cache_control {:type "ephemeral"}}))) |
27 | 27 |
|
28 | 28 | (defn ^:private base-request! [{:keys [rid body api-url api-key content-block* on-error on-response]}] |
29 | | - (let [url (str api-url messages-path)] |
| 29 | + (let [url (str api-url messages-path) |
| 30 | + reason-id (str (random-uuid))] |
30 | 31 | (llm-util/log-request logger-tag rid url body) |
31 | 32 | (http/post |
32 | 33 | url |
|
46 | 47 | (with-open [rdr (io/reader body)] |
47 | 48 | (doseq [[event data] (llm-util/event-data-seq rdr)] |
48 | 49 | (llm-util/log-response logger-tag rid event data) |
49 | | - (on-response event data content-block*)))) |
| 50 | + (on-response event data content-block* reason-id)))) |
50 | 51 | (catch Exception e |
51 | 52 | (on-error {:exception e})))) |
52 | 53 | (fn [e] |
|
77 | 78 |
|
78 | 79 | (defn completion! |
79 | 80 | [{:keys [model user-prompt temperature instructions max-output-tokens |
80 | | - api-url api-key past-messages tools web-search] |
| 81 | + api-url api-key reason-tokens past-messages tools web-search] |
81 | 82 | :or {temperature 1.0}} |
82 | | - {:keys [on-message-received on-error on-prepare-tool-call on-tool-called]}] |
| 83 | + {:keys [on-message-received on-error on-reason on-prepare-tool-call on-tool-called]}] |
83 | 84 | (let [messages (conj (past-messages->messages past-messages) |
84 | 85 | {:role "user" :content [{:type :text |
85 | 86 | :text user-prompt}]}) |
86 | | - body {:model model |
87 | | - :messages (add-cache-to-last-message messages) |
88 | | - :max_tokens max-output-tokens |
89 | | - :temperature temperature |
90 | | - ;; TODO support :thinking |
91 | | - :stream true |
92 | | - :tools (->tools tools web-search) |
93 | | - :system [{:type "text" :text instructions :cache_control {:type "ephemeral"}}]} |
| 87 | + body (assoc-some |
| 88 | + {:model model |
| 89 | + :messages (add-cache-to-last-message messages) |
| 90 | + :max_tokens max-output-tokens |
| 91 | + :temperature temperature |
| 92 | + :stream true |
| 93 | + :tools (->tools tools web-search) |
| 94 | + :system [{:type "text" :text instructions :cache_control {:type "ephemeral"}}]} |
| 95 | + :thinking (when (and reason-tokens (> reason-tokens 0)) |
| 96 | + {:type "enabled" |
| 97 | + :budget_tokens reason-tokens})) |
| 98 | + |
94 | 99 | on-response-fn |
95 | | - (fn handle-response [event data content-block*] |
| 100 | + (fn handle-response [event data content-block* reason-id] |
96 | 101 | (case event |
97 | 102 | "content_block_start" (case (-> data :content_block :type) |
| 103 | + "thinking" (do |
| 104 | + (on-reason {:status :started |
| 105 | + :id reason-id}) |
| 106 | + (swap! content-block* assoc (:index data) (:content_block data))) |
98 | 107 | "tool_use" (do |
99 | 108 | (on-prepare-tool-call {:name (-> data :content_block :name) |
100 | 109 | :id (-> data :content_block :id) |
101 | 110 | :arguments-text ""}) |
102 | 111 | (swap! content-block* assoc (:index data) (:content_block data))) |
103 | 112 |
|
104 | 113 | nil) |
| 114 | + "content_block_stop" (when-let [content-block (get @content-block* (:index data))] |
| 115 | + (case (:type content-block) |
| 116 | + "thinking" (on-reason {:status :finished |
| 117 | + :id reason-id}) |
| 118 | + nil) |
| 119 | + (swap! content-block* dissoc (:index data))) |
105 | 120 | "content_block_delta" (case (-> data :delta :type) |
106 | 121 | "text_delta" (on-message-received {:type :text |
107 | 122 | :text (-> data :delta :text)}) |
|
117 | 132 | :title (-> data :delta :citation :title) |
118 | 133 | :url (-> data :delta :citation :url)}) |
119 | 134 | nil) |
| 135 | + "thinking_delta" (on-reason {:status :thinking |
| 136 | + :id reason-id |
| 137 | + :text (-> data :delta :thinking)}) |
120 | 138 | (logger/warn "Unkown response delta type" (-> data :delta :type))) |
121 | 139 | "message_delta" (case (-> data :delta :stop_reason) |
122 | 140 | "tool_use" (doseq [content-block (vals @content-block*)] |
|
0 commit comments