|
45 | 45 | (cb chunk))) |
46 | 46 | (throw (ex-info "Failed to call OpenAI API" {:body (slurp (:body response))}))))) |
47 | 47 |
|
48 | | -(defn call-function |
| 48 | +(defn call-function |
49 | 49 | " returns channel that will emit one message and then close" |
50 | 50 | [function-handler function-name arguments tool-call-id] |
51 | 51 | (let [c (async/chan)] |
52 | 52 | (try |
53 | 53 | (function-handler |
54 | | - function-name |
55 | | - arguments |
56 | | - {:resolve |
57 | | - (fn [output] |
58 | | - (jsonrpc/notify :message {:content (format "\n## ROLE tool (%s)\n%s\n" function-name output)}) |
59 | | - (async/go |
60 | | - (async/>! c {:content output :role "tool" :tool_call_id tool-call-id}) |
61 | | - (async/close! c))) |
62 | | - :fail (fn [output] |
63 | | - (jsonrpc/notify :message {:content (format "\n## ROLE tool\n function call %s failed %s" function-name output)}) |
64 | | - (async/go |
65 | | - (async/>! c {:content output :role "tool" :tool_call_id tool-call-id}) |
66 | | - (async/close! c)))}) |
| 54 | + function-name |
| 55 | + arguments |
| 56 | + {:resolve |
| 57 | + (fn [output] |
| 58 | + (jsonrpc/notify :message {:content (format "\n## ROLE tool (%s)\n%s\n" function-name output)}) |
| 59 | + (async/go |
| 60 | + (async/>! c {:content output :role "tool" :tool_call_id tool-call-id}) |
| 61 | + (async/close! c))) |
| 62 | + :fail (fn [output] |
| 63 | + (jsonrpc/notify :message {:content (format "\n## ROLE tool\n function call %s failed %s" function-name output)}) |
| 64 | + (async/go |
| 65 | + (async/>! c {:content output :role "tool" :tool_call_id tool-call-id}) |
| 66 | + (async/close! c)))}) |
67 | 67 | (catch Throwable t |
68 | 68 | ;; function-handlers should handle this on their own but this is just in case |
69 | 69 | (async/go |
|
115 | 115 | (let [response (atom {})] |
116 | 116 | (async/go-loop |
117 | 117 | [] |
118 | | - (let [{:keys [finish-reason] :as e} (async/<! c)] |
119 | | - (cond |
120 | | - (:done e) (let [{calls :tool-calls content :content finish-reason :finish-reason} @response |
121 | | - messages [(merge |
122 | | - {:role "assistant"} |
123 | | - (when (seq (vals calls)) |
124 | | - {:tool_calls (->> (vals calls) |
125 | | - (map #(assoc % :type "function")))}) |
126 | | - (when content {:content content}))]] |
127 | | - |
128 | | - (jsonrpc/notify :functions-done (or (vals calls) "")) |
| 118 | + (let [e (async/<! c)] |
| 119 | + (if (:done e) |
| 120 | + (let [{calls :tool-calls content :content finish-reason :finish-reason} @response |
| 121 | + messages [(merge |
| 122 | + {:role "assistant"} |
| 123 | + (when (seq (vals calls)) |
| 124 | + {:tool_calls (->> (vals calls) |
| 125 | + (map #(assoc % :type "function")))}) |
| 126 | + (when content {:content content}))]] |
| 127 | + |
| 128 | + (jsonrpc/notify :message {:debug (str @response)}) |
| 129 | + (jsonrpc/notify :functions-done (or (vals calls) "")) |
129 | 130 | ;; make-tool-calls returns a channel with results of tool call messages |
130 | 131 | ;; so we can continue the conversation |
131 | | - {:finish-reason finish-reason |
132 | | - :messages |
133 | | - (async/<! |
134 | | - (->> |
135 | | - (make-tool-calls |
136 | | - (:tool-handler e) |
137 | | - (vals calls)) |
138 | | - (async/reduce conj messages)))}) |
139 | | - (:content e) (do |
140 | | - (swap! response update-in [:content] (fnil str "") (:content e)) |
141 | | - (when finish-reason (swap! response assoc :finish-reason finish-reason)) |
142 | | - (jsonrpc/notify :message {:content (:content e)}) |
143 | | - (recur)) |
144 | | - :else (let [{:keys [tool_calls finish-reason]} e] |
145 | | - (swap! response update-tool-calls tool_calls) |
146 | | - (when finish-reason (swap! response assoc :finish-reason finish-reason)) |
147 | | - (jsonrpc/notify :functions (->> @response :tool-calls vals)) |
148 | | - (recur))))))) |
| 132 | + {:finish-reason finish-reason |
| 133 | + :messages |
| 134 | + (async/<! |
| 135 | + (->> |
| 136 | + (make-tool-calls |
| 137 | + (:tool-handler e) |
| 138 | + (vals calls)) |
| 139 | + (async/reduce conj messages)))}) |
| 140 | + |
| 141 | + (let [{:keys [content tool_calls finish-reason]} e] |
| 142 | + (when content |
| 143 | + (swap! response update-in [:content] (fnil str "") content) |
| 144 | + (jsonrpc/notify :message {:content content})) |
| 145 | + (when tool_calls |
| 146 | + (swap! response update-tool-calls tool_calls) |
| 147 | + (jsonrpc/notify :functions (->> @response :tool-calls vals))) |
| 148 | + (when finish-reason (swap! response assoc :finish-reason finish-reason)) |
| 149 | + |
| 150 | + (recur))))))) |
149 | 151 |
|
150 | 152 | (defn parse [s] |
151 | 153 | (if (= "[DONE]" (string/trim s)) |
|
167 | 169 | (some-> chunk |
168 | 170 | (string/replace #"data: " "") |
169 | 171 | (parse))] |
| 172 | + ;; messages will either have a delta, a message, or just a finish_reason, |
| 173 | + ;; depending on whether it's streaming. Usually, the finish_reason doesn't |
| 174 | + ;; occur on it's own. |
170 | 175 | (try |
171 | 176 | (cond |
172 | 177 | done? (async/>!! |
|
176 | 181 | (when finish_reason {:finish-reason finish_reason}))) |
177 | 182 |
|
178 | 183 | ;; streaming |
179 | | - delta (cond |
180 | | - (:content delta) (async/>!! c (merge |
181 | | - {:content (:content delta)} |
182 | | - (when finish_reason {:finish-reason finish_reason}))) |
183 | | - |
184 | | - (:tool_calls delta) (async/>!! c (merge |
185 | | - delta |
186 | | - (when finish_reason {:finish-reason finish_reason}))) |
187 | | - finish_reason (async/>!! c {:finish-reason finish_reason})) |
| 184 | + delta |
| 185 | + (async/>!! c (merge |
| 186 | + delta |
| 187 | + (when finish_reason {:finish-reason finish_reason}))) |
188 | 188 |
|
189 | 189 | ;; non-streaming |
190 | | - message (cond |
191 | | - (:content message) (do (async/>!! c (merge |
192 | | - message |
193 | | - (when finish_reason {:finish-reason finish_reason}))) |
194 | | - (async/>!! c {:done true :tool-handler function-handler})) |
195 | | - (:tool_calls message) (do |
196 | | - (async/>!! c (merge |
197 | | - message |
198 | | - (when finish_reason {:finish-reason finish_reason}))) |
199 | | - (async/>!! c {:done true :tool-handler function-handler}))) |
| 190 | + message |
| 191 | + (do |
| 192 | + (async/>!! c (merge |
| 193 | + message |
| 194 | + (when finish_reason {:finish-reason finish_reason}))) |
| 195 | + (async/>!! c {:done true :tool-handler function-handler})) |
200 | 196 | finish_reason (async/>!! c {:finish-reason finish_reason})) |
201 | 197 | (catch Throwable _))))])) |
202 | 198 |
|
|
0 commit comments