emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/llm b438ae7a72 46/71: Merge pull request #35 from r0man


From: ELPA Syncer
Subject: [elpa] externals/llm b438ae7a72 46/71: Merge pull request #35 from r0man/plz
Date: Fri, 17 May 2024 00:58:47 -0400 (EDT)

branch: externals/llm
commit b438ae7a72e7304941a6f15df469aa8c3492a0a5
Merge: 1fb8511867 ccfe066654
Author: Andrew Hyatt <ahyatt@gmail.com>
Commit: GitHub <noreply@github.com>

    Merge pull request #35 from r0man/plz
    
    Error handling & JSON parsing
---
 llm-gemini.el       |  17 ++--
 llm-ollama.el       |  17 ++--
 llm-openai.el       |   2 -
 llm-request-plz.el  |  53 +++++-------
 llm-vertex.el       |  17 ++--
 plz-event-source.el |  11 ++-
 plz-media-type.el   | 240 +++++++++++++++++++++++++++++++++++++---------------
 plz.el              |   1 +
 8 files changed, 228 insertions(+), 130 deletions(-)

diff --git a/llm-gemini.el b/llm-gemini.el
index 9915601b30..7e86b3b112 100644
--- a/llm-gemini.el
+++ b/llm-gemini.el
@@ -65,7 +65,6 @@ You can get this at https://makersuite.google.com/app/apikey.";
   (let ((buf (current-buffer)))
     (llm-request-plz-async (llm-gemini--embedding-url provider)
                            :data (llm-gemini--embedding-request provider 
string)
-                           :media-type '(application/json)
                            :on-success (lambda (data)
                                          (llm-request-callback-in-buffer
                                           buf vector-callback 
(llm-gemini--embedding-response-handler data)))
@@ -112,7 +111,6 @@ If STREAMING-P is non-nil, use the streaming endpoint."
   (let ((buf (current-buffer)))
     (llm-request-plz-async (llm-gemini--chat-url provider nil)
                            :data (llm-gemini--chat-request prompt)
-                           :media-type '(application/json)
                            :on-success (lambda (data)
                                          (llm-request-callback-in-buffer
                                           buf response-callback
@@ -131,12 +129,15 @@ If STREAMING-P is non-nil, use the streaming endpoint."
      (llm-gemini--chat-url provider t)
      :data (llm-gemini--chat-request prompt)
      :on-element (lambda (element)
-                   (when-let ((response (llm-vertex--get-chat-response 
element)))
-                     (if (stringp response)
-                         (when (> (length response) 0)
-                           (setq streamed-text (concat streamed-text response))
-                           (llm-request-callback-in-buffer buf 
partial-callback streamed-text))
-                       (setq function-call response))))
+                   (if (alist-get 'error element)
+                       (llm-request-callback-in-buffer buf error-callback 
'error
+                                                       
(llm-vertex--error-message element))
+                     (when-let ((response (llm-vertex--get-chat-response 
element)))
+                       (if (stringp response)
+                           (when (> (length response) 0)
+                             (setq streamed-text (concat streamed-text 
response))
+                             (llm-request-callback-in-buffer buf 
partial-callback streamed-text))
+                         (setq function-call response)))))
      :on-success (lambda (data)
                    (llm-request-callback-in-buffer
                     buf response-callback
diff --git a/llm-ollama.el b/llm-ollama.el
index e36701ff25..0a80e9955f 100644
--- a/llm-ollama.el
+++ b/llm-ollama.el
@@ -86,7 +86,6 @@ PROVIDER is the llm-ollama provider."
   (let ((buf (current-buffer)))
     (llm-request-plz-async (llm-ollama--url provider "embeddings")
                            :data (llm-ollama--embedding-request provider 
string)
-                           :media-type '(application/json)
                            :on-success (lambda (data)
                                          (llm-request-callback-in-buffer
                                           buf vector-callback 
(llm-ollama--embedding-extract-response data)))
@@ -148,18 +147,16 @@ STREAMING is a boolean to control whether to stream the 
response."
     (llm-request-plz-async
      (llm-ollama--url provider "chat")
      :data (llm-ollama--chat-request provider prompt nil)
-     :media-type '(application/json)
      :timeout llm-ollama-chat-timeout
      :on-success (lambda (data)
-                   (let ((output (llm-ollama--get-response data)))
-                     (llm-provider-utils-append-to-prompt prompt data)
-                     (llm-request-plz-callback-in-buffer buf response-callback 
output)))
-     :on-error (lambda (_ data)
-                 (let ((errdata (cdr (assoc 'error data))))
+                   (let ((response (llm-ollama--get-response data)))
+                     (llm-provider-utils-append-to-prompt prompt response)
+                     (llm-request-plz-callback-in-buffer buf response-callback 
response)))
+     :on-error (lambda (code data)
+                 (let ((error-message (cdr (assoc 'error data))))
                    (llm-request-plz-callback-in-buffer buf error-callback 
'error
                                                        (format "Problem 
calling Ollama: %s message: %s"
-                                                               (cdr (assoc 
'type errdata))
-                                                               (cdr (assoc 
'message errdata)))))))))
+                                                               code 
error-message)))))))
 
 (cl-defmethod llm-chat-streaming ((provider llm-ollama) prompt 
partial-callback response-callback error-callback)
   (let ((buf (current-buffer))
@@ -179,7 +176,7 @@ STREAMING is a boolean to control whether to stream the 
response."
       :on-error (lambda (_ _)
                   ;; The problem with ollama is that it doesn't
                   ;; seem to have an error response.
-                  (llm-request-callback-in-buffer buf error-callback "Unknown 
error calling ollama")))))
+                  (llm-request-callback-in-buffer buf error-callback 'error 
"Unknown error calling ollama")))))
 
 (cl-defmethod llm-name ((provider llm-ollama))
   (llm-ollama-chat-model provider))
diff --git a/llm-openai.el b/llm-openai.el
index 28396ce045..e444824262 100644
--- a/llm-openai.el
+++ b/llm-openai.el
@@ -116,7 +116,6 @@ This is just the key, if it exists."
   (llm-openai--check-key provider)
   (let ((buf (current-buffer)))
     (llm-request-plz-async (llm-openai--url provider "embeddings")
-                           :media-type '(application/json)
                            :headers (llm-openai--headers provider)
                            :data (llm-openai--embedding-request 
(llm-openai-embedding-model provider) string)
                            :on-success (lambda (data)
@@ -229,7 +228,6 @@ PROMPT is the prompt that needs to be updated with the 
response."
   (let ((buf (current-buffer)))
     (llm-request-plz-async
      (llm-openai--url provider "chat/completions")
-     :media-type '(application/json)
      :headers (llm-openai--headers provider)
      :data (llm-openai--chat-request (llm-openai-chat-model provider) prompt)
      :on-success (lambda (data)
diff --git a/llm-request-plz.el b/llm-request-plz.el
index a9cf9f63e9..fa7a9e5284 100644
--- a/llm-request-plz.el
+++ b/llm-request-plz.el
@@ -27,7 +27,7 @@
 (require 'rx)
 (require 'url-http)
 
-(defcustom llm-request-plz-timeout 60
+(defcustom llm-request-plz-timeout (* 2 60)
   "The number of seconds to wait for a response from a HTTP server.
 
 Request timings are depending on the request. Requests that need
@@ -101,19 +101,24 @@ code and the HTTP body of the error response.
 
 For Curl errors, ON-ERROR will be called with the exit code of
 the curl process and an error message."
-  (cond ((plz-error-response error)
-         (let ((response (plz-error-response error)))
-           (funcall on-error
-                    (plz-response-status response)
-                    (plz-response-body response))))
+  (cond ((plz-media-type-filter-error-p error)
+         (let ((cause (plz-media-type-filter-error-cause error))
+               (response (plz-error-response error)))
+           ;; TODO: What do we want to pass to callers here?
+           (funcall on-error 'filter-error cause)))
         ((plz-error-curl-error error)
          (let ((curl-error (plz-error-curl-error error)))
            (funcall on-error
                     (car curl-error)
                     (cdr curl-error))))
+        ((plz-error-response error)
+         (when-let ((response (plz-error-response error))
+                    (status (plz-response-status response))
+                    (body (plz-response-body response)))
+           (funcall on-error status body)))
         (t (user-error "Unexpected error: %s" error))))
 
-(cl-defun llm-request-plz-async (url &key headers data on-success 
on-success-raw media-type
+(cl-defun llm-request-plz-async (url &key headers data on-success media-type
                                      on-error timeout)
   "Make a request to URL.
 Nothing will be returned.
@@ -131,30 +136,21 @@ and required otherwise.
 ON-ERROR will be called with the error code and a response-body.
 This is required.
 
-MEDIA-TYPE is a required argument that sets a media type, useful
-for streaming formats.  It is expected that this is only used by
-other methods in this file.
-
-ON-SUCCESS-RAW, if set, will be called in the buffer with the
-response body, and expect the response content. This is an
-optional argument, and mostly useful for streaming.  If not set,
-the buffer is turned into JSON and passed to ON-SUCCESS."
-  (unless media-type
-    (error "MEDIA-TYPE is required in llm-request-plz-async"))
+MEDIA-TYPE is an optional argument that adds or overrides a media
+type, useful for streaming formats.  It is expected that this is
+only used by other methods in this file."
   (plz-media-type-request
     'post url
-    :as `(media-types ,(cons media-type plz-media-types))
+    :as `(media-types ,(if media-type
+                           (cons media-type plz-media-types)
+                         plz-media-types))
     :body (when data
             (encode-coding-string (json-encode data) 'utf-8))
     :headers (append headers
                      '(("Content-Type" . "application/json")))
     :then (lambda (response)
-            (let ((response (plz-response-body response)))
-              (when on-success-raw
-                (funcall on-success-raw response))
-              (when on-success
-                (funcall on-success (when (and response (> (length response) 
0))
-                                      (json-read-from-string response))))))
+            (when on-success
+              (funcall on-success (plz-response-body response))))
     :else (lambda (error)
             (when on-error
               (llm-request-plz--handle-error error on-error)))
@@ -184,9 +180,7 @@ This is required.
                          :headers headers
                          :data data
                          :on-error on-error
-                         ;; Have to use :on-success-raw because :on-success 
will try to
-                         ;; convert to JSON, and this already should be JSON.
-                         :on-success-raw on-success
+                         :on-success on-success
                          :timeout timeout
                          :media-type
                          (cons 'application/json
@@ -216,10 +210,7 @@ This is required.
                          :headers headers
                          :data data
                          :on-error on-error
-                         ;; Have to use :on-success-raw because :on-success 
will try to
-                         ;; convert to JSON, and this already should be JSON.
-                         :on-success-raw (lambda (resp)
-                                           (funcall on-success 
(plz-response-body resp)))
+                         :on-success on-success
                          :timeout timeout
                          :media-type
                          (cons 'application/x-ndjson
diff --git a/llm-vertex.el b/llm-vertex.el
index ec16a2462d..3ee7083f0e 100644
--- a/llm-vertex.el
+++ b/llm-vertex.el
@@ -130,7 +130,6 @@ KEY-GENTIME keeps track of when the key was generated, 
because the key must be r
      (llm-vertex--embedding-url provider)
      :headers `(("Authorization" . ,(format "Bearer %s" (llm-vertex-key 
provider))))
      :data `(("instances" . [(("content" . ,string))]))
-     :media-type '(application/json)
      :on-success (lambda (data)
                    (llm-request-callback-in-buffer
                     buf vector-callback 
(llm-vertex--embedding-extract-response data)))
@@ -302,7 +301,6 @@ If STREAMING is non-nil, use the URL for the streaming API."
      (llm-vertex--chat-url provider)
      :headers `(("Authorization" . ,(format "Bearer %s" (llm-vertex-key 
provider))))
      :data (llm-vertex--chat-request prompt)
-     :media-type '(application/json)
      :on-success (lambda (data)
                    (llm-request-callback-in-buffer
                     buf response-callback
@@ -322,12 +320,15 @@ If STREAMING is non-nil, use the URL for the streaming 
API."
      :headers `(("Authorization" . ,(format "Bearer %s" (llm-vertex-key 
provider))))
      :data (llm-vertex--chat-request prompt)
      :on-element (lambda (element)
-                   (when-let ((response (llm-vertex--get-chat-response 
element)))
-                     (if (stringp response)
-                         (when (> (length response) 0)
-                           (setq streamed-text (concat streamed-text response))
-                           (llm-request-callback-in-buffer buf 
partial-callback streamed-text))
-                       (setq function-call response))))
+                   (if (alist-get 'error element)
+                       (llm-request-callback-in-buffer buf error-callback 
'error
+                                                       
(llm-vertex--error-message element))
+                     (when-let ((response (llm-vertex--get-chat-response 
element)))
+                       (if (stringp response)
+                           (when (> (length response) 0)
+                             (setq streamed-text (concat streamed-text 
response))
+                             (llm-request-callback-in-buffer buf 
partial-callback streamed-text))
+                         (setq function-call response)))))
      :on-success (lambda (data)
                    (llm-request-callback-in-buffer
                     buf response-callback
diff --git a/plz-event-source.el b/plz-event-source.el
index c6861f7deb..b522cf0c1b 100644
--- a/plz-event-source.el
+++ b/plz-event-source.el
@@ -405,7 +405,16 @@
    (events :documentation "Association list from event type to handler."
            :initarg :events
            :initform nil
-           :type list)))
+           :type list))
+  "A media type class that handles the processing of HTTP responses
+in the server sent events format.  The HTTP response is processed
+in a streaming way.  The :events slot of the class can be set to
+an association list from event type symbol to a handler function.
+Whenever a new event is parsed and emitted the handler for the
+corresponding event type will be called with two arguments, an
+instance of the underlying event source class and an event.  The
+body slot of the plz-response struct passed to the THEN and ELSE
+callbacks will always be set to nil.")
 
 (defvar-local plz-event-source--current nil
   "The event source of the current buffer.")
diff --git a/plz-media-type.el b/plz-media-type.el
index 23008e49c7..c20855487d 100644
--- a/plz-media-type.el
+++ b/plz-media-type.el
@@ -4,6 +4,10 @@
 
 ;; Author: r0man <roman@burningswell.com>
 ;; Maintainer: r0man <roman@burningswell.com>
+;; URL: https://github.com/r0man/plz-media-type.el
+;; Version: 0.1-pre
+;; Package-Requires: ((emacs "26.3"))
+;; Keywords: comm, network, http
 
 ;; This file is part of GNU Emacs.
 
@@ -24,7 +28,13 @@
 
 ;;; Commentary:
 
-;; This file handles content type.
+;; This library provides enhanced handling of MIME types for HTTP
+;; requests within Emacs.  It utilizes the 'plz' library for
+;; networking calls, extending it to process responses based on the
+;; Content-Type header.  This library defines various classes and
+;; methods for parsing and processing standard MIME types, including
+;; JSON, XML, HTML, and binary data.  It allows for extensible
+;; processing of additional types through subclassing.
 
 ;;; Code:
 
@@ -34,6 +44,13 @@
 (require 'eieio)
 (require 'plz)
 
+(define-error 'plz-media-type-filter-error
+              "plz-media-type: Error in process filter"
+              'plz-error)
+
+(cl-defstruct (plz-media-type-filter-error (:include plz-error))
+  cause)
+
 (defclass plz-media-type ()
   ((type
     :documentation "The media type."
@@ -47,7 +64,11 @@
     :documentation "The parameters of the media type."
     :initarg :parameters
     :initform nil
-    :subtype list)))
+    :subtype list))
+  "A class that hold information about the type, subtype and
+parameters of a media type.  It is meant to be sub-classed to
+handle the processing of different media types and supports the
+processing of streaming and non-streaming HTTP responses.")
 
 (defun plz-media-type-charset (media-type)
   "Return the character set of the MEDIA-TYPE."
@@ -158,15 +179,18 @@ CHUNK is a part of the HTTP body."
                        (media-type (plz-media-type--of-response media-types 
response))
                        (coding-system (plz-media-type-coding-system 
media-type)))
                   (setq-local plz-media-type--current media-type)
+                  (setq-local plz-media-type--response
+                              (make-plz-response
+                               :headers (plz-response-headers response)
+                               :status (plz-response-status response)
+                               :version (plz-response-version response)))
                   (when-let (body (plz-response-body response))
                     (when (> (length body) 0)
                       (setf (plz-response-body response)
                             (decode-coding-string body coding-system))
                       (delete-region body-start (point-max))
                       (set-marker (process-mark process) (point))
-                      (plz-media-type-process media-type process response)))
-                  (setf (plz-response-body response) nil)
-                  (setq-local plz-media-type--response response))))))
+                      (plz-media-type-process media-type process 
response))))))))
         (when moving
           (goto-char (process-mark process)))))))
 
@@ -174,13 +198,17 @@ CHUNK is a part of the HTTP body."
 
 (defclass plz-media-type:application/octet-stream (plz-media-type)
   ((type :initform 'application)
-   (subtype :initform 'octet-stream)))
+   (subtype :initform 'octet-stream))
+  "Media type class that handles the processing of octet stream
+HTTP responses.  The media type sets the body slot of the
+plz-response structure to the unmodified value of the HTTP response
+body.  It is used as the default media type processor.")
 
 (cl-defmethod plz-media-type-else ((media-type 
plz-media-type:application/octet-stream) error)
   "Transform the ERROR into a format suitable for MEDIA-TYPE."
-  (let ((response (plz-error-response error)))
-    (setf (plz-error-response error) (plz-media-type-then media-type response))
-    error))
+  (when-let (response (plz-error-response error))
+    (setf (plz-error-response error) (plz-media-type-then media-type 
response)))
+  error)
 
 (cl-defmethod plz-media-type-then ((media-type 
plz-media-type:application/octet-stream) response)
   "Transform the RESPONSE into a format suitable for MEDIA-TYPE."
@@ -221,7 +249,15 @@ defaults to `nil`."
 be `hash-table', `alist' (the default) or `plist'."
     :initarg :object-type
     :initform 'alist
-    :type symbol)))
+    :type symbol))
+  "Media type class that handles the processing of HTTP responses
+in the JSON format.  The HTTP response is processed in a
+non-streaming way.  After the response has been received, the
+body of the plz-response structure is set to the result of parsing
+the HTTP response body with the `json-parse-buffer' function.
+The arguments to the `json-parse-buffer' can be customized by
+making an instance of this class and setting its slots
+accordingly.")
 
 (defun plz-media-type--parse-json-object (media-type)
   "Parse the JSON object in the current buffer according to MEDIA-TYPE."
@@ -240,9 +276,17 @@ be `hash-table', `alist' (the default) or `plist'."
 
 (defclass plz-media-type:application/json-array 
(plz-media-type:application/json)
   ((handler
-    :documentation "A function that will be called for each object in the JSON 
array."
+    :documentation "Function that will be called for each object in the JSON 
array."
     :initarg :handler
-    :type (or function symbol))))
+    :type (or function symbol)))
+  "Media type class that handles the processing of HTTP responses
+in a JSON format that assumes that the object at the top level is
+an array.  The HTTP response is processed in a streaming way.
+Each object in the top level array will be parsed with the
+`json-parse-buffer' function.  The function in the :handler slot
+will be called each time a new object arrives.  The body slot of
+the plz-response structure passed to the THEN and ELSE callbacks
+will always be set to nil.")
 
 (defun plz-media-type:application/json-array--parse-next (media-type)
   "Parse a single line of the newline delimited JSON MEDIA-TYPE."
@@ -306,10 +350,18 @@ be `hash-table', `alist' (the default) or `plist'."
 (defclass plz-media-type:application/x-ndjson (plz-media-type:application/json)
   ((subtype :initform 'x-ndjson)
    (handler
-    :documentation "A function that will be called for each line that contains 
a JSON object."
+    :documentation "Function that will be called for each line that contains a 
JSON object."
     :initarg :handler
     :initform nil
-    :type (or function null symbol))))
+    :type (or function null symbol)))
+  "Media type class that handles the processing of HTTP responses
+in a JSON format that assumes that the object at the top level is
+an array.  The HTTP response is processed in a streaming way.
+Each object in the top level array will be parsed with the
+`json-parse-buffer' function.  The function in the :handler slot
+will be called each time a new object arrives.  The body slot of
+the plz-response structure passed to the THEN and ELSE callbacks
+will always be set to nil.")
 
 (defconst plz-media-type:application/x-ndjson--line-regexp
   (rx (* not-newline) (or "\r\n" "\n" "\r"))
@@ -342,24 +394,38 @@ be `hash-table', `alist' (the default) or `plist'."
 (cl-defmethod plz-media-type-then ((media-type 
plz-media-type:application/x-ndjson) response)
   "Transform the RESPONSE into a format suitable for MEDIA-TYPE."
   (plz-media-type:application/x-ndjson--parse-stream media-type)
+  (setf (plz-response-body response) nil)
   response)
 
 ;; Content Type: application/xml
 
 (defclass plz-media-type:application/xml 
(plz-media-type:application/octet-stream)
-  ((subtype :initform 'xml)))
+  ((subtype :initform 'xml))
+  "Media type class that handles the processing of HTTP responses
+in the XML format.  The HTTP response is processed in a
+non-streaming way.  After the response has been received, the
+body of the plz-response structure is set to the result of parsing
+the HTTP response body with the `libxml-parse-html-region'
+function.")
 
 (cl-defmethod plz-media-type-then ((media-type plz-media-type:application/xml) 
response)
   "Transform the RESPONSE into a format suitable for MEDIA-TYPE."
   (with-slots (array-type false-object null-object object-type) media-type
-    (setf (plz-response-body response) (libxml-parse-html-region))
+    (setf (plz-response-body response)
+          (libxml-parse-html-region (point-min) (point-max) nil))
     response))
 
 ;; Content Type: text/html
 
 (defclass plz-media-type:text/html (plz-media-type:application/xml)
   ((type :initform 'text)
-   (subtype :initform 'xml)))
+   (subtype :initform 'xml))
+  "Media type class that handles the processing of HTTP responses
+in the HTML format.  The HTTP response is processed in a
+non-streaming way.  After the response has been received, the
+body of the plz-response structure is set to the result of parsing
+the HTTP response body with the `libxml-parse-html-region'
+function.")
 
 (defvar plz-media-types
   `((application/json . ,(plz-media-type:application/json))
@@ -367,7 +433,7 @@ be `hash-table', `alist' (the default) or `plist'."
     (application/xml . ,(plz-media-type:application/xml))
     (text/html . ,(plz-media-type:text/html))
     (t . ,(plz-media-type:application/octet-stream)))
-  "Alist from media type to content type.")
+  "Association list from media type to content type.")
 
 (defun plz-media-type--handle-sync-http-error (error media-types)
   "Handle the synchronous HTTP ERROR using MEDIA-TYPES."
@@ -387,9 +453,15 @@ be `hash-table', `alist' (the default) or `plist'."
 
 (defun plz-media-type--handle-sync-error (error media-types)
   "Handle the synchronous ERROR using MEDIA-TYPES."
-  (if (eq 'plz-http-error (car error))
-      (plz-media-type--handle-sync-http-error error media-types)
-    (signal (car error) (cdr error))))
+  (cond
+   ((plz-media-type-filter-error-p error)
+    (signal 'plz-media-type-filter-error
+            (list (plz-media-type-filter-error-message error)
+                  (plz-media-type-filter-error-response error)
+                  (plz-media-type-filter-error-cause error))))
+   ((eq 'plz-http-error (car error))
+    (plz-media-type--handle-sync-http-error error media-types))
+   (t (signal (car error) (cdr error)))))
 
 (cl-defun plz-media-type-request
     (method
@@ -435,11 +507,26 @@ It may be:
   non-existent file; if it exists, it will not be overwritten,
   and an error will be signaled.
 
-- `(stream :through PROCESS-FILTER)' to asynchronously stream the
-  HTTP response.  PROCESS-FILTER is an Emacs process filter
-  function, and must accept two arguments: the curl process
-  sending the request and a chunk of the HTTP body, which was
-  just received.
+- `(media-types MEDIA-TYPES)' to handle the processing of the
+  response based on the Content-Type header.  MEDIA-TYPES is an
+  association list from a content type symbol to an instance of a
+  `plz-media-type' class.  The `plz-media-types' variable is
+  bound to an association list and can be used to handle some
+  commonly used formats such as JSON, HTML, XML.  This list can
+  be used as a basis and is meant to be extended by users.  If no
+  media type was found for a content type, it will be handled by
+  the default octet stream media type.  When this option is used,
+  the THEN callback will always receive a plz-response structure as
+  argument, and the ELSE callback always a plz-error structure.  The
+  plz-response structure will always have the status and header
+  slots set.  The body slot depends on the media type
+  implementation.  In the case for JSON, HTML, XML it will
+  contain the decoded response body.  When receiving JSON for
+  example, it will be an Emacs Lisp association list.  For
+  streaming responses like text/event-stream it will be set to
+  nil, and the events of the server sent events specification
+  will be dispatched to the handlers registered with the media
+  type instance.
 
 - A function, which is called in the response buffer with it
   narrowed to the response body (suitable for, e.g. `json-read').
@@ -493,51 +580,64 @@ not.
   (if-let (media-types (pcase as
                          (`(media-types ,media-types)
                           media-types)))
-      (condition-case error
-          (let* ((buffer)
-                 (plz-curl-default-args (cons "--no-buffer" 
plz-curl-default-args))
-                 (result (plz method url
-                           :as 'buffer
-                           :body body
-                           :body-type body-type
-                           :connect-timeout connect-timeout
-                           :decode decode
-                           :else (lambda (error)
-                                   (setq buffer (current-buffer))
-                                   (when (or (functionp else) (symbolp else))
-                                     (funcall else (plz-media-type-else
-                                                    plz-media-type--current
-                                                    error))))
-                           :finally (lambda ()
-                                      (unwind-protect
-                                          (when (functionp finally)
-                                            (funcall finally))
-                                        (when (buffer-live-p buffer)
-                                          (kill-buffer buffer))))
-                           :headers headers
-                           :noquery noquery
-                           :process-filter (lambda (process chunk)
-                                             (plz-media-type-process-filter 
process media-types chunk))
-                           :timeout timeout
-                           :then (if (symbolp then)
-                                     then
-                                   (lambda (_)
+      (let ((buffer) (filter-error))
+        (condition-case error
+            (let* ((plz-curl-default-args (cons "--no-buffer" 
plz-curl-default-args))
+                   (result (plz method url
+                             :as 'buffer
+                             :body body
+                             :body-type body-type
+                             :connect-timeout connect-timeout
+                             :decode decode
+                             :else (lambda (error)
                                      (setq buffer (current-buffer))
-                                     (when (or (functionp then) (symbolp then))
-                                       (funcall then (plz-media-type-then
-                                                      plz-media-type--current
-                                                      
plz-media-type--response))))))))
-            (cond ((bufferp result)
-                   (unwind-protect
-                       (with-current-buffer result
-                         (plz-media-type-then plz-media-type--current 
plz-media-type--response))
-                     (when (buffer-live-p result)
-                       (kill-buffer result))))
-                  ((processp result)
-                   result)
-                  (t (user-error "Unexpected response: %s" result))))
-        ;; TODO: How to kill the buffer for sync requests that raise an error?
-        (plz-error (plz-media-type--handle-sync-error error media-types)))
+                                     (when (or (functionp else) (symbolp else))
+                                       (funcall else (or filter-error
+                                                         (plz-media-type-else
+                                                          
plz-media-type--current
+                                                          error)))))
+                             :finally (lambda ()
+                                        (unwind-protect
+                                            (when (functionp finally)
+                                              (funcall finally))
+                                          (when (buffer-live-p buffer)
+                                            (kill-buffer buffer))))
+                             :headers headers
+                             :noquery noquery
+                             :process-filter (lambda (process chunk)
+                                               (condition-case cause
+                                                   
(plz-media-type-process-filter process media-types chunk)
+                                                 (error
+                                                  (let ((buffer 
(process-buffer process)))
+                                                    (setq filter-error
+                                                          
(make-plz-media-type-filter-error
+                                                           :cause cause
+                                                           :message (format 
"error in process filter: %S" cause)
+                                                           :response (when 
(buffer-live-p buffer)
+                                                                       
(with-current-buffer buffer
+                                                                         
plz-media-type--response))))
+                                                    (delete-process 
process)))))
+                             :timeout timeout
+                             :then (if (symbolp then)
+                                       then
+                                     (lambda (_)
+                                       (setq buffer (current-buffer))
+                                       (when (or (functionp then) (symbolp 
then))
+                                         (funcall then (plz-media-type-then
+                                                        plz-media-type--current
+                                                        
plz-media-type--response))))))))
+              (cond ((bufferp result)
+                     (unwind-protect
+                         (with-current-buffer result
+                           (plz-media-type-then plz-media-type--current 
plz-media-type--response))
+                       (when (buffer-live-p result)
+                         (kill-buffer result))))
+                    ((processp result)
+                     result)
+                    (t (user-error "Unexpected response: %s" result))))
+          ;; TODO: How to kill the buffer for sync requests that raise an 
error?
+          (plz-error
+           (plz-media-type--handle-sync-error (or filter-error error) 
media-types))))
     (apply #'plz (append (list method url) rest))))
 
 ;;;; Footer
diff --git a/plz.el b/plz.el
index 69072063c7..3a9271bca6 100644
--- a/plz.el
+++ b/plz.el
@@ -755,6 +755,7 @@ argument passed to `plz--sentinel', which see."
         (pcase-exhaustive status
           ((or 0 "finished\n")
            ;; Curl exited normally: check HTTP status code.
+           (widen)
            (goto-char (point-min))
            (plz--skip-proxy-headers)
            (while (plz--skip-redirect-headers))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]