branch: externals/llm commit a9cd296cd812576aa101c866acba7dd2e4d1b325 Author: Roman Scherer <ro...@burningswell.com> Commit: Roman Scherer <ro...@burningswell.com>
Add llm-request-plz.el Add a copy of the llm-request module that uses plz instead. --- llm-request-plz.el | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/llm-request-plz.el b/llm-request-plz.el new file mode 100644 index 0000000000..d103f10448 --- /dev/null +++ b/llm-request-plz.el @@ -0,0 +1,146 @@ +;;; llm-request-plz.el --- Curl request handling code -*- lexical-binding: t; package-lint-main-file: "llm.el"; -*- + +;; Copyright (c) 2023 Free Software Foundation, Inc. + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; This file provides basic functions for providers who need to request data. It +;; assumes the server is using json. + +;;; Code: +(require 'cl-macs) +(require 'json) +(require 'plz-event-source) +(require 'plz-media-type) +(require 'rx) +(require 'url-http) + +(defcustom llm-request-plz-timeout 60 + "The number of seconds to wait for a response from a HTTP server. + +Request timings are depending on the request. Requests that need +more output may take more time, and there is other processing +besides just token generation that can take a while. Sometimes +the LLM can get stuck, and you don't want it to take too long. +This should be balanced to be good enough for hard requests but +not very long so that we can end stuck requests." + :type 'integer + :group 'llm) + +(cl-defun llm-request-plz-sync-raw-output (url &key headers data timeout) + "Make a request to URL. The raw text response 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. + +TIMEOUT is the number of seconds to wait for a response." + (condition-case error + (plz-media-type-request + 'post url + :as `(media-types ,plz-media-types) + :body (when data + (encode-coding-string (json-encode data) 'utf-8)) + :headers (append headers '(("Content-Type" . "application/json"))) + :timeout (or timeout llm-request-plz-timeout)) + (plz-error + (seq-let [error-sym message data] error + (cond + ((eq 'plz-http-error error-sym) + (let ((response (plz-error-response data))) + (error "LLM request failed with code %d: %s (additional information: %s)" + (plz-response-status response) + (nth 2 (assq (plz-response-status response) url-http-codes)) + (plz-response-body response)))) + ((and (eq 'plz-curl-error error-sym) + (eq 28 (car (plz-error-curl-error data)))) + (error "LLM request timed out")) + (t (signal error-sym (list message data)))))))) + +(cl-defun llm-request-plz-sync (url &key headers data timeout) + "Make a request to URL. The parsed response 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. + +TIMEOUT is the number of seconds to wait for a response." + (llm-request-plz-sync-raw-output url + :headers headers + :data data + :timeout timeout)) + +(cl-defun llm-request-plz-async (url &key headers data on-success on-success-raw on-error _on-partial 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. + +ON-ERROR will be called with the error code and a response-body. +This is required. + +ON-PARTIAL will be called with the potentially incomplete response +body as a string. This is an optional argument. + +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." + (plz-media-type-request + 'post url + :as `(media-types ,(cons (cons "text/event-stream" + (plz-media-type:text/event-stream + :events 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-raw + (user-error "Not supported yet: on-success-raw")) + (when on-success + (funcall on-success 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) + "Run F with ARSG in the context of BUF. +But if BUF has been killed, use a temporary buffer instead. +If F is nil, nothing is done." + (when f + (if (buffer-live-p buf) + (with-current-buffer buf (apply f args)) + (with-temp-buffer (apply f args))))) + +(provide 'llm-request-plz) +;;; llm-request-plz.el ends here