branch: externals/llm commit 3988fecb534c77624b2ba421f717f51a95a6e5a7 Author: Andrew Hyatt <ahy...@gmail.com> Commit: Andrew Hyatt <ahy...@gmail.com>
Make separate function for event streaming, w/ client-side handlers --- llm-openai.el | 54 +++++++++++++++++++++++++++++------------------------- llm-request-plz.el | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 25 deletions(-) diff --git a/llm-openai.el b/llm-openai.el index 00c2765dc1..1a3d89b7a6 100644 --- a/llm-openai.el +++ b/llm-openai.el @@ -296,31 +296,35 @@ RESPONSE can be nil if the response is complete." response-callback error-callback) (llm-openai--check-key provider) (let ((buf (current-buffer))) - (llm-request-plz-async (llm-openai--url provider "chat/completions") - :headers (llm-openai--headers provider) - :data (llm-openai--chat-request (llm-openai-chat-model provider) prompt t) - :on-error (lambda (_ data) - (let ((errdata (cdr - (assoc 'error - (json-read-from-string data))))) - (llm-request-callback-in-buffer - buf error-callback 'error - (format "Problem calling Open AI: %s message: %s" - (cdr (assoc 'type errdata)) - (cdr (assoc 'message errdata)))))) - :on-partial (lambda (data) - (when (not (equal data "[DONE]")) - (when-let ((response (llm-openai--get-partial-chat-response - (json-read-from-string data)))) - (when (stringp response) - (llm-request-callback-in-buffer buf partial-callback response))))) - :on-success (lambda (_) - (llm-request-callback-in-buffer - buf - response-callback - (llm-openai--process-and-return - provider prompt nil - error-callback)))))) + (llm-request-plz-event-stream + (llm-openai--url provider "chat/completions") + :headers (llm-openai--headers provider) + :data (llm-openai--chat-request (llm-openai-chat-model provider) prompt t) + :event-stream-handlers + `(("message" . ,(lambda (data) + (when (not (equal data "[DONE]")) + (when-let ((response (llm-openai--get-partial-chat-response + (json-read-from-string data)))) + (when (stringp response) + (llm-request-callback-in-buffer buf partial-callback response)))))) + ("error" . ,(lambda (data) + (llm-request-callback-in-buffer + buf error-callback 'error data)))) + :on-error (lambda (_ data) + (let ((errdata + (cdr (assoc 'error (json-read-from-string data))))) + (llm-request-callback-in-buffer + buf error-callback 'error + (format "Problem calling Open AI: %s message: %s" + (cdr (assoc 'type errdata)) + (cdr (assoc 'message errdata)))))) + :on-success (lambda (_) + (llm-request-callback-in-buffer + buf + response-callback + (llm-openai--process-and-return + provider prompt nil + error-callback)))))) (cl-defmethod llm-name ((_ llm-openai)) "Open AI") diff --git a/llm-request-plz.el b/llm-request-plz.el index d7bd3813b2..67285eb1b0 100644 --- a/llm-request-plz.el +++ b/llm-request-plz.el @@ -146,6 +146,55 @@ the buffer is turned into JSON and passed to ON-SUCCESS." (funcall on-error error))) :timeout (or timeout llm-request-plz-timeout))) +(cl-defun llm-request-plz-event-stream (url &key headers data on-error on-success + event-stream-handlers timeout) + "Make a request to URL. +Nothing will be returned. + +HEADERS will be added in the Authorization header, in addition to +standard json header. This is optional. + +DATA will be jsonified and sent as the request body. +This is required. + +ON-SUCCESS will be called with the response body as a json +object. This is optional in the case that ON-SUCCESS-DATA is set, +and required otherwise. + +EVENT-STREAM-HANDLERS are an alist of event names to functions +that handle the event's corresponding data, which will be called +with the new event data as a string. + +ON-ERROR will be called with the error code and a response-body. +This is required. +" + (plz-media-type-request + 'post url + :as `(media-types + ,(cons + (cons "text/event-stream" + (plz-media-type:text/event-stream + ;; Convert so that each event handler gets the body, not the + ;; `plz-response' itself. + :events (mapcar + (lambda (cons) + (cons (car cons) + (lambda (_ resp) (funcall (cdr cons) (plz-event-source-event-data resp))))) + event-stream-handlers))) + plz-media-types)) + :body (when data + (encode-coding-string (json-encode data) 'utf-8)) + :headers (append headers + '(("Accept-encoding" . "identity") + ("Content-Type" . "application/json"))) + :then (lambda (response) + (when on-success + (funcall on-success (plz-response-body response)))) + :else (lambda (error) + (when on-error + (funcall on-error error))) + :timeout (or timeout llm-request-plz-timeout))) + ;; This is a useful method for getting out of the request buffer when it's time ;; to make callbacks. (defun llm-request-plz-callback-in-buffer (buf f &rest args)