From fb4785c347666f1b873e4d632885ba733b829b7b Mon Sep 17 00:00:00 2001 From: Neale Swinnerton Date: Tue, 4 Apr 2023 13:26:08 +0100 Subject: [PATCH] refactor: move creation of headers inside openai-request (wip) All the callers of openai-request pass the Content-Type, Authorization headers and specify the parser as json-read. This change makes the defaults be :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " (openai--resolve-key openai-key))) :parser 'json-read This is allow the key resolution call to be common (and private). --- openai-audio.el | 3 --- openai-chat.el | 9 --------- openai-completion.el | 3 --- openai-edit.el | 3 --- openai-embedding.el | 3 --- openai-engine.el | 6 ------ openai-file.el | 15 --------------- openai-fine-tune.el | 18 ------------------ openai-image.el | 7 ------- openai-model.el | 6 ------ openai-moderation.el | 3 --- openai.el | 44 +++++++++++++++++++++++++++++++++----------- 12 files changed, 33 insertions(+), 87 deletions(-) diff --git a/openai-audio.el b/openai-audio.el index 16b0371..2134f68 100644 --- a/openai-audio.el +++ b/openai-audio.el @@ -54,8 +54,6 @@ for more information. Arguments here refer to MODEL PROMPT, RESPONSE-FORMAT, TEMPERATURE, and LANGUAGE." (openai-request "https://api.openai.com/v1/audio/transcriptions" :type "POST" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("model" . ,model) ("file" . ,file) @@ -63,7 +61,6 @@ TEMPERATURE, and LANGUAGE." ("response_format" . ,response-format) ("temperature" . ,temperature) ("language" . ,language))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) diff --git a/openai-chat.el b/openai-chat.el index b98efb6..a8b11ae 100644 --- a/openai-chat.el +++ b/openai-chat.el @@ -34,7 +34,6 @@ ;;;###autoload (cl-defun openai-chat ( messages callback &key - (key openai-key) (model "gpt-3.5-turbo") temperature top-p @@ -59,8 +58,6 @@ for more information. Arguments here refer to MODEL, TEMPERATURE, TOP-P, N, STREAM, STOP, MAX-TOKENS, PRESENCE-PENALTY, FREQUENCY-PENALTY, and LOGIT-BIAS." (openai-request "https://api.openai.com/v1/chat/completions" :type "POST" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " (openai--resolve-key key)))) :data (openai--json-encode `(("model" . ,model) ("messages" . ,messages) @@ -74,7 +71,6 @@ STREAM, STOP, MAX-TOKENS, PRESENCE-PENALTY, FREQUENCY-PENALTY, and LOGIT-BIAS." ("frequency_penalty" . ,frequency-penalty) ("logit_bias" . ,logit-bias) ("user" . ,user))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -92,11 +88,6 @@ STREAM, STOP, MAX-TOKENS, PRESENCE-PENALTY, FREQUENCY-PENALTY, and LOGIT-BIAS." :type 'number :group 'openai) -(defun openai--resolve-key (key) - (if (functionp key) - (funcall key) - key)) - ;;;###autoload (defun openai-chat-say () "Start making a conversation to OpenAI. diff --git a/openai-completion.el b/openai-completion.el index 80db56e..a493c80 100644 --- a/openai-completion.el +++ b/openai-completion.el @@ -65,8 +65,6 @@ TEMPERATURE, TOP-P, N, STREAM, LOGPROBS, ECHO, STOP, PRESENCE-PENALTY, FREQUENCY-PENALTY, BEST-OF, and LOGIT-BIAS." (openai-request "https://api.openai.com/v1/completions" :type "POST" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("model" . ,model) ("prompt" . ,prompt) @@ -84,7 +82,6 @@ FREQUENCY-PENALTY, BEST-OF, and LOGIT-BIAS." ("best_of" . ,best-of) ("logit_bias" . ,logit-bias) ("user" . ,user))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) diff --git a/openai-edit.el b/openai-edit.el index ce7b220..483a569 100644 --- a/openai-edit.el +++ b/openai-edit.el @@ -53,8 +53,6 @@ The rest of the arugments are optional, please see OpenAI API reference page for more information. Arguments here refer to TEMPERATURE, TOP-P, and N." (openai-request "https://api.openai.com/v1/edits" :type "POST" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("model" . ,model) ("input" . ,input) @@ -62,7 +60,6 @@ for more information. Arguments here refer to TEMPERATURE, TOP-P, and N." ("temperature" . ,temperature) ("top_p" . ,top-p) ("n" . ,n))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) diff --git a/openai-embedding.el b/openai-embedding.el index 1ebd828..0584545 100644 --- a/openai-embedding.el +++ b/openai-embedding.el @@ -53,13 +53,10 @@ The rest of the arugments are optional, please see OpenAI API reference page for more information. Arguments here refer to MODEL." (openai-request "https://api.openai.com/v1/embeddings" :type "POST" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("model" . ,model) ("input" . ,input) ("user" . ,user))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) diff --git a/openai-engine.el b/openai-engine.el index 759d6bc..eed964e 100644 --- a/openai-engine.el +++ b/openai-engine.el @@ -46,9 +46,6 @@ Arguments KEY is global option; however, you can overwrite the value by passing it in." (openai-request "https://api.openai.com/v1/engines" :type "GET" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -67,9 +64,6 @@ Arguments KEY is global option; however, you can overwrite the value by passing it in." (openai-request (format "https://api.openai.com/v1/engines/%s" engine-id) :type "GET" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) diff --git a/openai-file.el b/openai-file.el index 9844460..6914ca7 100644 --- a/openai-file.el +++ b/openai-file.el @@ -43,9 +43,6 @@ Arguments KEY is global option; however, you can overwrite the value by passing it in." (openai-request "https://api.openai.com/v1/files" :type "GET" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -72,12 +69,9 @@ Arguments KEY is global option; however, you can overwrite the value by passing it in." (openai-request "https://api.openai.com/v1/files" :type "POST" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("file" . ,file) ("purpose" . ,purpose))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -95,11 +89,8 @@ Arguments KEY is global option; however, you can overwrite the value by passing it in." (openai-request "https://api.openai.com/v1/files" :type "DELETE" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("file_id" . ,file-id))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -117,11 +108,8 @@ Arguments KEY is global option; however, you can overwrite the value by passing it in." (openai-request (format "https://api.openai.com/v1/files/%s" file-id) :type "GET" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("file_id" . ,file-id))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -139,11 +127,8 @@ Arguments KEY is global option; however, you can overwrite the value by passing it in." (openai-request (format "https://api.openai.com/v1/files/%s/content" file-id) :type "GET" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("file_id" . ,file-id))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) diff --git a/openai-fine-tune.el b/openai-fine-tune.el index a426a41..fe9ba73 100644 --- a/openai-fine-tune.el +++ b/openai-fine-tune.el @@ -65,8 +65,6 @@ COMPUTE-CLASSIFICATION-METRICS, CLASSIFICATION-N-CLASSES, CLASSIFICATION-POSITIVE-CLASS, CLASSIFICATION-BETAS, and SUFFIX" (openai-request "https://api.openai.com/v1/fine-tunes" :type "POST" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("model" . ,model) ("training_file" . ,training-file) @@ -80,7 +78,6 @@ CLASSIFICATION-POSITIVE-CLASS, CLASSIFICATION-BETAS, and SUFFIX" ("classification_positive_class" . ,classification-positive-class) ("classification_betas" . ,classification-betas) ("suffix" . ,suffix))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -93,9 +90,6 @@ CLASSIFICATION-POSITIVE-CLASS, CLASSIFICATION-BETAS, and SUFFIX" The argument CALLBACK is execuated after request is made." (openai-request "https://api.openai.com/v1/fine-tunes" :type "GET" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -110,9 +104,6 @@ The FINE-TUNE-ID of the fine-tune job. The argument CALLBACK is execuated after request is made." (openai-request (format "https://api.openai.com/v1/fine-tunes/%s" fine-tune-id) :type "GET" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -127,9 +118,6 @@ The FINE-TUNE-ID of the fine-tune job to cancel. The argument CALLBACK is execuated after request is made." (openai-request (format "https://api.openai.com/v1/fine-tunes/%s/cancel" fine-tune-id) :type "POST" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -144,9 +132,6 @@ The FINE-TUNE-ID of the fine-tune job to get events for. The argument CALLBACK is execuated after request is made." (openai-request (format "https://api.openai.com/v1/fine-tunes/%s/events" fine-tune-id) :type "GET" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -161,9 +146,6 @@ The MODEL to delete. The argument CALLBACK is execuated after request is made." (openai-request (format "https://api.openai.com/v1/models/%s" model) :type "DELETE" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) diff --git a/openai-image.el b/openai-image.el index 2f7a95d..dc7c836 100644 --- a/openai-image.el +++ b/openai-image.el @@ -51,15 +51,12 @@ The rest of the arugments are optional, please see OpenAI API reference page for more information. Arguments here refer to N, SIZE, and RESPONSE-FORMAT." (openai-request "https://api.openai.com/v1/images/generations" :type "POST" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("prompt" . ,prompt) ("n" . ,n) ("size" . ,size) ("response_format" . ,response-format) ("user" . ,user))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -86,7 +83,6 @@ for more information. Arguments here refer to MASK, N, SIZE, and RESPONSE-FORMAT." (openai-request "https://api.openai.com/v1/images/edits" :type "POST" - :headers `(("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("image" . ,image) ("prompt" . ,prompt) @@ -95,7 +91,6 @@ RESPONSE-FORMAT." ("size" . ,size) ("response_format" . ,response-format) ("user" . ,user))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -121,7 +116,6 @@ for more information. Arguments here refer to MASK, N, SIZE, and RESPONSE-FORMAT." (openai-request "https://api.openai.com/v1/images/variations" :type "POST" - :headers `(("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("image" . ,image) ("mask" . ,mask) @@ -129,7 +123,6 @@ RESPONSE-FORMAT." ("size" . ,size) ("response_format" . ,response-format) ("user" . ,user))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) diff --git a/openai-model.el b/openai-model.el index 14533f0..6893a8e 100644 --- a/openai-model.el +++ b/openai-model.el @@ -38,9 +38,6 @@ Arguments KEY is global options; however, you can overwrite the value by passing it in." (openai-request "https://api.openai.com/v1/models" :type "GET" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) @@ -54,9 +51,6 @@ Arguments KEY is global options; however, you can overwrite the value by passing it in." (openai-request (format "https://api.openai.com/v1/models/%s" model) :type "GET" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) diff --git a/openai-moderation.el b/openai-moderation.el index ce5b6d1..f8f1136 100644 --- a/openai-moderation.el +++ b/openai-moderation.el @@ -46,12 +46,9 @@ Arguments KEY is global options; however, you can overwrite the value by passing it in." (openai-request "https://api.openai.com/v1/embeddings" :type "POST" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) :data (openai--json-encode `(("model" . ,model) ("input" . ,input))) - :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) (funcall callback data))))) diff --git a/openai.el b/openai.el index 861cb4b..57672b7 100644 --- a/openai.el +++ b/openai.el @@ -70,14 +70,20 @@ (funcall (plist-get (car auth-info) :secret)) (error "OpenAI API key not found in auth-source"))) +(defun openai--resolve-key (key) + "If the given KEY is a function call it and return the result, otherwise return KEY." + (cond + ((functionp key) (funcall key)) + ((not (string-empty-p key)) key) + (t (user-error "[INFO] Invalid API key, please set it to the correct value: %s" openai-key)))) + (defvar openai-key "" "Variable storing the openai key or a function name to retrieve it. The function should take no arguments and return a string containing the key. A function, `openai-key-auth-source', that retrieves the key from -auth-source is provided for convenience. -") +auth-source is provided for convenience.") (defvar openai-user "" @@ -116,20 +122,36 @@ See https://beta.openai.com/docs/guides/error-codes/api-errors." (defvar openai-error nil "Records for the last error.") +(defun openai--add-missing-defaults (p) + "Add Content-Type and Authorization headers to BODY if not present." + + (message "%s" p) + (setq headers (plist-get p :headers)) + (unless (assq "Content-Type" headers) + (setq headers (cons '("Content-Type" . "application/json") headers))) + (unless (assq "Authorization" headers) + (setq headers (cons `("Authorization" . ,(concat "Bearer " (openai--resolve-key openai-key))) headers))) + (setq parser (if-let ((parser (plist-get p :parser))) + parser + 'json-read)) + (thread-first p + (plist-put :parser parser) + (plist-put :headers `(quote ,headers)))) + (defmacro openai-request (url &rest body) "Wrapper for `request' function. The URL is the url for `request' function; then BODY is the arguments for rest." (declare (indent 1)) - `(if (string-empty-p openai-key) - (user-error "[INFO] Invalid API key, please set it to the correct value: %s" openai-key) - (setq openai-error nil) - (request ,url - :error (cl-function - (lambda (&key response &allow-other-keys) - (setq openai-error response) - (openai--handle-error response))) - ,@body))) + `(progn + (setq openai-error nil) + (request ,url + :error (cl-function + (lambda (&key response &allow-other-keys) + (setq openai-error response) + (openai--handle-error response))) + ,@(openai--add-missing-defaults body) + ))) ;; ;;; Util