Function: gptel-curl--stream-filter

gptel-curl--stream-filter is a natively compiled function defined in gptel-request.el.

Signature

(gptel-curl--stream-filter PROCESS OUTPUT)

Documentation

Process OUTPUT from a Curl PROCESS for gptel.

Run the response callback with any completed chunks in OUTPUT.

Source Code

;; Defined in /nix/store/q3r7g81pvghw7a523kjk2k2zih3pfk8z-emacs-packages-deps/share/emacs/site-lisp/elpa/gptel-20261002.545/gptel-request.el
(defun gptel-curl--stream-filter (process output)
  "Process OUTPUT from a Curl PROCESS for gptel.

Run the response callback with any completed chunks in OUTPUT."
  (let* ((fsm (car (alist-get process gptel--request-alist)))
         (proc-info (gptel-fsm-info fsm))
         (callback (or (plist-get proc-info :callback)
                       #'gptel-curl--stream-insert-response)))
    (with-current-buffer (process-buffer process)
      ;; Insert output
      (save-excursion
        (goto-char (process-mark process))
        (insert output)
        (set-marker (process-mark process) (point)))

      ;; Find HTTP status
      (unless (plist-get proc-info :http-status)
        (save-excursion
          (goto-char (point-min))
          (when-let* (((not (= (line-end-position) (point-max))))
                      (http-msg (buffer-substring (line-beginning-position)
                                                  (line-end-position)))
                      (http-status
                       (save-match-data
                         (and (string-match "HTTP/[.0-9]+ +\\([0-9]+\\)" http-msg)
                              (match-string 1 http-msg)))))
            (plist-put proc-info :http-status http-status)
            (plist-put proc-info :status (string-trim http-msg))
            (gptel--fsm-transition fsm)))) ;Response started, WAIT -> TYPE

      (when-let* ((http-msg (plist-get proc-info :status))
                  (http-status (plist-get proc-info :http-status)))
        ;; Find data chunk(s) and run callback
        ;; FIXME Handle the case where HTTP 100 is followed by HTTP (not 200) BUG #194
        (when (member http-status '("200" "100"))
          (let ((response (gptel-curl--parse-stream
                           (plist-get proc-info :backend) proc-info))
                (reasoning-block (plist-get proc-info :reasoning-block)))
            ;; Depending on the API, there are two modes that reasoning or
            ;; chain-of-thought content appears: as part of the main response
            ;; but surrounded by <think>...</think> tags, or as a separate
            ;; JSON field in the response stream.
            ;;
            ;; These cases are handled using two PROC-INFO keys:
            ;;
            ;; :reasoning-block is nil before checking for reasoning, 'in when
            ;; in a reasoning block, t when we reach the end of the block, and
            ;; 'done afterwards or if no reasoning block is found.  This
            ;; applies to both the modes above.
            ;;
            ;; :reasoning contains the reasoning text parsed from the separate
            ;; JSON field.
            ;;
            ;; NOTE: We assume here that the reasoning block always
            ;; precedes the main response block.
            (unless (eq reasoning-block 'done)
              (let ((reasoning (plist-get proc-info :reasoning)))
                (cond
                 ((stringp reasoning)
                  ;; Obtained from separate JSON field in response
                  (funcall callback (cons 'reasoning reasoning) proc-info)
                  (unless reasoning-block ;Record that we're in a reasoning block (#709)
                    (plist-put proc-info :reasoning-block 'in))
                  (plist-put proc-info :reasoning nil)) ;Reset for next parsing round
                 ((and (string-blank-p response) ;Defer checking if response is blank
                       (not reasoning-block))) ;unless we're in a reasoning block already
                 ((and (null reasoning-block) (length> response 0))
                  ;; Obtained from main response stream: reasoning block start
                  (if-let*  ((idx (string-match-p "<think>" response)))
                      (progn
                        (when (> idx 0) ;Collect leading whitespace before <think>
                          (funcall callback (substring response 0 idx) proc-info)
                          (setq response (substring response idx)))
                        (setq response (cons 'reasoning response))
                        (plist-put proc-info :reasoning-block 'in))
                    (plist-put proc-info :reasoning-block 'done)))
                 ((and (not (eq reasoning-block t)) (length> response 0))
                  (if-let* ((idx (string-match-p "</think>" response)))
                      (progn
                        (funcall callback
                                 (cons 'reasoning (substring response nil (+ idx 8)))
                                 proc-info)
                        (setq reasoning-block t) ;Signal end of reasoning stream
                        (plist-put proc-info :reasoning-block t)
                        (setq response (substring response (+ idx 8))))
                    (setq response (cons 'reasoning response)))))
                (when (eq reasoning-block t) ;End of reasoning block
                  (funcall callback '(reasoning . t) proc-info)
                  (plist-put proc-info :reasoning-block 'done))))
            (unless (equal response "") ;Response callback
              (funcall callback response proc-info))))))))