From 935911b19557fb8d52fc1dce9b248c76260d9684 Mon Sep 17 00:00:00 2001 From: JenChieh Date: Thu, 6 Apr 2023 00:28:58 -0700 Subject: [PATCH 1/4] feat: Impls auth-source and refactor end points --- README.md | 8 +++++ openai-audio.el | 18 +++++++----- openai-chat.el | 9 +++--- openai-completion.el | 9 +++--- openai-edit.el | 9 +++--- openai-embedding.el | 9 +++--- openai-engine.el | 14 +++++---- openai-file.el | 56 +++++++++++++++++++---------------- openai-fine-tune.el | 69 +++++++++++++++++++++++++++++--------------- openai-image.el | 25 +++++++++------- openai-model.el | 23 ++++++++------- openai-moderation.el | 9 +++--- openai.el | 50 ++++++++++++++++++++++++++++++-- 13 files changed, 202 insertions(+), 106 deletions(-) diff --git a/README.md b/README.md index 17d8c55..b9b6fce 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,14 @@ You will need to set up your API key before you can use this library. (setq openai-key "[YOUR API KEY]") ``` +Alternatively you can configure a function to retrieve the key from some +external source. A function, `openai-key-auth-source` is provided to retrieve +the key from an auth-source entry under the `:host` key `api.openai.com` + +```elisp +(setq openai-key #'openai-key-auth-source) +``` + For requests that need your user identifier, ```elisp diff --git a/openai-audio.el b/openai-audio.el index 16b0371..736ff0a 100644 --- a/openai-audio.el +++ b/openai-audio.el @@ -34,7 +34,9 @@ ;;;###autoload (cl-defun openai-audio-create-transcription ( file callback &key + (content-type openai-content-type) (key openai-key) + org-id (model "whisper-1") prompt response-format @@ -46,16 +48,15 @@ Argument FILE is audio file to transcribe, in one of these formats: mp3, mp4, mpeg, mpga, m4a, wav, or webm. CALLBACK is the execuation after request is made. -Arguments KEY is global options; however, you can overwrite the value by passing -it in. +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; however, you +can overwrite the value by passing it in. The rest of the arugments are optional, please see OpenAI API reference page 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("model" . ,model) ("file" . ,file) @@ -71,7 +72,9 @@ TEMPERATURE, and LANGUAGE." ;;;###autoload (cl-defun openai-audio-create-translation ( file callback &key + (content-type openai-content-type) (key openai-key) + org-id (model "whisper-1") prompt response-format @@ -81,16 +84,15 @@ TEMPERATURE, and LANGUAGE." Argument FILE is the audio file to translate, in one of these formats: mp3, mp4, mpeg, mpga, m4a, wav, or webm. CALLBACK is the execuation after request is made. -Arguments KEY is global options; however, you can overwrite the value by passing -it in. +Arguments CONTENT-TYPE, KEY and ORG-ID are global options; however, you +can overwrite the value by passing it in. The rest of the arugments are optional, please see OpenAI API reference page for more information. Arguments here refer to MODEL PROMPT, RESPONSE-FORMAT, and TEMPERATURE." (openai-request "https://api.openai.com/v1/audio/transcriptions" :type "POST" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("model" . ,model) ("file" . ,file) diff --git a/openai-chat.el b/openai-chat.el index c2c84e5..76dd808 100644 --- a/openai-chat.el +++ b/openai-chat.el @@ -34,7 +34,9 @@ ;;;###autoload (cl-defun openai-chat ( messages callback &key + (content-type openai-content-type) (key openai-key) + org-id (model "gpt-3.5-turbo") temperature top-p @@ -51,16 +53,15 @@ Arguments MESSAGES and CALLBACK are required for this type of request. MESSAGES is the conversation data. CALLBACK is the execuation after request is made. -Arguments KEY and USER are global options; however, you can overwrite the value -by passing it in. +Arguments CONTENT-TYPE, KEY, ORG-ID and USER are global options; however, you +can overwrite the value by passing it in. The rest of the arugments are optional, please see OpenAI API reference page 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 " key))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("model" . ,model) ("messages" . ,messages) diff --git a/openai-completion.el b/openai-completion.el index 80db56e..496f556 100644 --- a/openai-completion.el +++ b/openai-completion.el @@ -34,7 +34,9 @@ ;;;###autoload (cl-defun openai-completion ( prompt callback &key + (content-type openai-content-type) (key openai-key) + org-id (model "text-davinci-003") suffix max-tokens @@ -56,8 +58,8 @@ Arguments PROMPT and CALLBACK are required for this type of request. PROMPT is either the question or instruction to OpenAI. CALLBACK is the execuation after request is made. -Arguments KEY and USER are global options; however, you can overwrite the value -by passing it in. +Arguments CONTENT-TYPE, KEY, ORG-ID and USER are global options; however, you +can overwrite the value by passing it in. The rest of the arugments are optional, please see OpenAI API reference page for more information. Arguments here refer to MODEL, SUFFIX, MAX-TOKENS, @@ -65,8 +67,7 @@ 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("model" . ,model) ("prompt" . ,prompt) diff --git a/openai-edit.el b/openai-edit.el index ce7b220..c7d1ef4 100644 --- a/openai-edit.el +++ b/openai-edit.el @@ -34,7 +34,9 @@ (cl-defun openai-edit-create ( input instruction callback &key + (content-type openai-content-type) (key openai-key) + org-id (model "text-davinci-edit-001") temperature top-p @@ -46,15 +48,14 @@ tells the model how to edit the prompt. The argument CALLBACK is execuated after request is made. -Arguments KEY is global options; however, you can overwrite the value by passing -it in. +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; however, you +can overwrite the value by passing it in. 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("model" . ,model) ("input" . ,input) diff --git a/openai-embedding.el b/openai-embedding.el index 1ebd828..bb5a95d 100644 --- a/openai-embedding.el +++ b/openai-embedding.el @@ -34,7 +34,9 @@ (cl-defun openai-embedding-create ( input callback &key + (content-type openai-content-type) (key openai-key) + org-id (model "text-embedding-ada-002") (user openai-user)) "Creates an embedding vector representing the input text. @@ -46,15 +48,14 @@ length. The argument CALLBACK is execuated after request is made. -Arguments KEY and USER are global options; however, you can overwrite the value -by passing it in. +Arguments CONTENT-TYPE, KEY, ORG-ID and USER are global options; however, you +can overwrite the value by passing it in. 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("model" . ,model) ("input" . ,input) diff --git a/openai-engine.el b/openai-engine.el index 759d6bc..dfc385d 100644 --- a/openai-engine.el +++ b/openai-engine.el @@ -36,7 +36,9 @@ (cl-defun openai-engine-list ( callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Lists the currently available (non-finetuned) models, and provides basic information about each one such as the owner and availability. @@ -46,8 +48,7 @@ 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))) + :headers (openai--headers content-type key org-id) :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) @@ -55,7 +56,9 @@ it in." (cl-defun openai-engine-retrieve ( engine-id callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Retrieves a model instance, providing basic information about it such as the owner and availability. @@ -67,8 +70,7 @@ 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))) + :headers (openai--headers content-type key org-id) :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) diff --git a/openai-file.el b/openai-file.el index 9844460..2bb8d7e 100644 --- a/openai-file.el +++ b/openai-file.el @@ -34,17 +34,18 @@ (cl-defun openai-file-list ( callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Return a list of files that belong to the user's organization. The argument CALLBACK is execuated after request is made. -Arguments KEY is global option; however, you can overwrite the value by passing -it in." +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; 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))) + :headers (openai--headers content-type key org-id) :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) @@ -52,7 +53,9 @@ it in." (cl-defun openai-file-upload ( file purpose callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Upload a file that contain document(s) to be used across various endpoints/features. @@ -68,12 +71,11 @@ uploaded file. Argument CALLBACK is function with data pass in. -Arguments KEY is global option; however, you can overwrite the value by passing -it in." +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("file" . ,file) ("purpose" . ,purpose))) @@ -84,19 +86,20 @@ it in." (cl-defun openai-file-delete ( file-id callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Delete a file. The arument FILE-ID is id of the file to use for this request. Argument CALLBACK is function with data pass in. -Arguments KEY is global option; however, you can overwrite the value by passing -it in." +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("file_id" . ,file-id))) :parser 'json-read @@ -106,19 +109,20 @@ it in." (cl-defun openai-file-retrieve ( file-id callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Return information about a specific file. The arument FILE-ID is id of the file to use for this request. The argument CALLBACK is execuated after request is made. -Arguments KEY is global option; however, you can overwrite the value by passing -it in." +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("file_id" . ,file-id))) :parser 'json-read @@ -128,19 +132,21 @@ it in." (cl-defun openai-file-retrieve-content ( file-id callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Return the contents of the specified file The arument FILE-ID is id of the file to use for this request. The argument CALLBACK is execuated after request is made. -Arguments KEY is global option; however, you can overwrite the value by passing -it in." + +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("file_id" . ,file-id))) :parser 'json-read diff --git a/openai-fine-tune.el b/openai-fine-tune.el index a426a41..7f7fbcf 100644 --- a/openai-fine-tune.el +++ b/openai-fine-tune.el @@ -33,7 +33,9 @@ (cl-defun openai-fine-tune-create ( training-file callback &key + (content-type openai-content-type) (key openai-key) + org-id (model "curie") validation-file n-epochs @@ -55,8 +57,8 @@ data. The argument CALLBACK is execuated after request is made. -Arguments KEY is global option; however, you can overwrite the value by passing -it in. +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; however, you +can overwrite the value by passing it in. The rest of the arugments are optional, please see OpenAI API reference page for more information. Arguments here refer to MODEL, VALIDATION-FILE, N-EPOCHS, @@ -65,8 +67,7 @@ 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("model" . ,model) ("training_file" . ,training-file) @@ -87,14 +88,18 @@ CLASSIFICATION-POSITIVE-CLASS, CLASSIFICATION-BETAS, and SUFFIX" (cl-defun openai-fine-tune-list ( callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "List your organization's fine-tuning jobs. -The argument CALLBACK is execuated after request is made." +The argument CALLBACK is execuated after request is made. + +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; however, you +can overwrite the value by passing it in." (openai-request "https://api.openai.com/v1/fine-tunes" :type "GET" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) + :headers (openai--headers content-type key org-id) :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) @@ -102,16 +107,20 @@ The argument CALLBACK is execuated after request is made." (cl-defun openai-fine-tune-retrieve ( fine-tune-id callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Gets info about the fine-tune job. The FINE-TUNE-ID of the fine-tune job. -The argument CALLBACK is execuated after request is made." +The argument CALLBACK is execuated after request is made. + +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; however, you +can overwrite the value by passing it in." (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))) + :headers (openai--headers content-type key org-id) :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) @@ -119,16 +128,20 @@ The argument CALLBACK is execuated after request is made." (cl-defun openai-fine-tune-cancel ( fine-tune-id callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Immediately cancel a fine-tune job. The FINE-TUNE-ID of the fine-tune job to cancel. -The argument CALLBACK is execuated after request is made." +The argument CALLBACK is execuated after request is made. + +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; however, you +can overwrite the value by passing it in." (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))) + :headers (openai--headers content-type key org-id) :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) @@ -136,16 +149,20 @@ The argument CALLBACK is execuated after request is made." (cl-defun openai-fine-tune-list-events ( fine-tune-id callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Get fine-grained status updates for a fine-tune job. The FINE-TUNE-ID of the fine-tune job to get events for. -The argument CALLBACK is execuated after request is made." +The argument CALLBACK is execuated after request is made. + +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; however, you +can overwrite the value by passing it in." (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))) + :headers (openai--headers content-type key org-id) :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) @@ -153,16 +170,20 @@ The argument CALLBACK is execuated after request is made." (cl-defun openai-fine-tune-delete ( model callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Delete a fine-tuned model. You must have the Owner role in your organization. The MODEL to delete. -The argument CALLBACK is execuated after request is made." +The argument CALLBACK is execuated after request is made. + +Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; however, you +can overwrite the value by passing it in." (openai-request (format "https://api.openai.com/v1/models/%s" model) :type "DELETE" - :headers `(("Content-Type" . "application/json") - ("Authorization" . ,(concat "Bearer " key))) + :headers (openai--headers content-type key org-id) :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) diff --git a/openai-image.el b/openai-image.el index 2f7a95d..6b530fc 100644 --- a/openai-image.el +++ b/openai-image.el @@ -33,7 +33,9 @@ (cl-defun openai-image ( prompt callback &key + (content-type openai-content-type) (key openai-key) + org-id n size response-format @@ -44,15 +46,14 @@ Arguments PROMPT and CALLBACK are required for this type of request. PROMPT is either the question or instruction to OpenAI. CALLBACK is the execuation after request is made. -Arguments KEY and USER are global options; however, you can overwrite the value -by passing it in. +Arguments CONTENT-TYPE, KEY, ORG-ID and USER are global options; however, you +can overwrite the value by passing it in. 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("prompt" . ,prompt) ("n" . ,n) @@ -66,7 +67,9 @@ for more information. Arguments here refer to N, SIZE, and RESPONSE-FORMAT." (cl-defun openai-image-edit ( image prompt callback &key + (content-type openai-content-type) (key openai-key) + org-id mask n size @@ -78,15 +81,15 @@ Arguments IMAGE, PROMPT and CALLBACK are required for this type of request. PROMPT is a text description of the desired image(s). IMAGE is the image file to edit. CALLBACK is the execuation after request is made. -Arguments KEY and USER are global options; however, you can overwrite the value -by passing it in. +Arguments CONTENT-TYPE, KEY, ORG-ID and USER are global options; however, you +can overwrite the value by passing it in. The rest of the arugments are optional, please see OpenAI API reference page 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("image" . ,image) ("prompt" . ,prompt) @@ -102,7 +105,9 @@ RESPONSE-FORMAT." (cl-defun openai-image-variation ( image callback &key + (content-type openai-content-type) (key openai-key) + org-id mask n size @@ -113,15 +118,15 @@ RESPONSE-FORMAT." Argument CALLBACK is function with data pass in, and the argument IMAGE must be a valid PNG file, less than 4MB, and square. -Arguments KEY and USER are global options; however, you can overwrite the value -by passing it in. +Arguments CONTENT-TYPE, KEY, ORG-ID and USER are global options; however, you +can overwrite the value by passing it in. The rest of the arugments are optional, please see OpenAI API reference page 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("image" . ,image) ("mask" . ,mask) diff --git a/openai-model.el b/openai-model.el index 14533f0..3aed716 100644 --- a/openai-model.el +++ b/openai-model.el @@ -31,15 +31,16 @@ (cl-defun openai-models ( callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Return models data and execute the CALLBACK. -Arguments KEY is global options; however, you can overwrite the value by passing -it in." +Arguments CONTENT-TYPE, KEY, and ORG-ID are 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))) + :headers (openai--headers content-type key org-id) :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) @@ -47,15 +48,17 @@ it in." (cl-defun openai-model ( model callback &key - (key openai-key)) + (content-type openai-content-type) + (key openai-key) + org-id) "Return MODEL data and execute the CALLBACK. -Arguments KEY is global options; however, you can overwrite the value by passing -it in." + +Arguments CONTENT-TYPE, KEY, and ORG-ID are 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))) + :headers (openai--headers content-type key org-id) :parser 'json-read :complete (cl-function (lambda (&key data &allow-other-keys) diff --git a/openai-moderation.el b/openai-moderation.el index ce5b6d1..f2eb475 100644 --- a/openai-moderation.el +++ b/openai-moderation.el @@ -34,7 +34,9 @@ (cl-defun openai-moderation-create ( input callback &key + (content-type openai-content-type) (key openai-key) + org-id (model "text-moderation-latest")) "Classifies if text violates OpenAI's Content Policy. @@ -42,12 +44,11 @@ Argument INPUT is the text to classify. The argument CALLBACK is execuated after request is made. -Arguments KEY is global options; however, you can overwrite the value by passing -it in." +Arguments CONTENT-TYPE, KEY, and ORG-ID are 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))) + :headers (openai--headers content-type key org-id) :data (openai--json-encode `(("model" . ,model) ("input" . ,input))) diff --git a/openai.el b/openai.el index b50cd95..d21fbc9 100644 --- a/openai.el +++ b/openai.el @@ -31,6 +31,7 @@ ;;; Code: +(require 'auth-source) (require 'cl-lib) (require 'let-alist) (require 'pcase) @@ -60,13 +61,57 @@ ;; ;;; Request +;;;###autoload +(defun openai-key-auth-source () + "Retrieve the OpenAI API key from auth-source." + (if-let ((auth-info (auth-source-search :max 1 + :host "api.openai.com" + :require '(:user :secret)))) + (funcall (plist-get (car auth-info) :secret)) + (error "OpenAI API key not found in auth-source"))) + +(defcustom openai-content-type "application/json" + "Default content type to make the request." + :type 'string + :group 'openai) + (defvar openai-key "" - "Generated API 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.") (defvar openai-user "" "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.") +(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)))) + +(defun open--alist-omit-null (alist) + "Omit null value or empty string in ALIST." + (cl-remove-if (lambda (pair) + (let ((value (cdr pair))) + (or (null value) ; ignore null + (and (stringp value) ; ignore empty string + (string-empty-p value))))) + alist)) + +(defun openai--headers (content-type key org-id) + "Construct request headers." + (open--alist-omit-null `(("Content-Type" . ,content-type) + ("Authorization" . ,(if (or (null key) + (string-empty-p key)) + "" + (concat "Bearer " key))) + ("OpenAI-Organization" . ,org-id)))) + (defun openai--json-encode (object) "Wrapper for function `json-encode' but it removes `nil' value before constructing JSON data. @@ -104,8 +149,7 @@ See https://beta.openai.com/docs/guides/error-codes/api-errors." 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) + `(progn (setq openai-error nil) (request ,url :error (cl-function From 9fe5566724c7d945463570d314c033cfacaa2460 Mon Sep 17 00:00:00 2001 From: JenChieh Date: Thu, 6 Apr 2023 00:34:53 -0700 Subject: [PATCH 2/4] specify content directly --- openai-audio.el | 4 ++-- openai-chat.el | 2 +- openai-completion.el | 2 +- openai-edit.el | 2 +- openai-embedding.el | 2 +- openai-engine.el | 4 ++-- openai-file.el | 10 +++++----- openai-fine-tune.el | 12 ++++++------ openai-image.el | 6 +++--- openai-model.el | 4 ++-- openai-moderation.el | 2 +- openai.el | 5 ----- 12 files changed, 25 insertions(+), 30 deletions(-) diff --git a/openai-audio.el b/openai-audio.el index 736ff0a..8ae4556 100644 --- a/openai-audio.el +++ b/openai-audio.el @@ -34,7 +34,7 @@ ;;;###autoload (cl-defun openai-audio-create-transcription ( file callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id (model "whisper-1") @@ -72,7 +72,7 @@ TEMPERATURE, and LANGUAGE." ;;;###autoload (cl-defun openai-audio-create-translation ( file callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id (model "whisper-1") diff --git a/openai-chat.el b/openai-chat.el index 76dd808..b4d586b 100644 --- a/openai-chat.el +++ b/openai-chat.el @@ -34,7 +34,7 @@ ;;;###autoload (cl-defun openai-chat ( messages callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id (model "gpt-3.5-turbo") diff --git a/openai-completion.el b/openai-completion.el index 496f556..58c5bd9 100644 --- a/openai-completion.el +++ b/openai-completion.el @@ -34,7 +34,7 @@ ;;;###autoload (cl-defun openai-completion ( prompt callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id (model "text-davinci-003") diff --git a/openai-edit.el b/openai-edit.el index c7d1ef4..16cf58a 100644 --- a/openai-edit.el +++ b/openai-edit.el @@ -34,7 +34,7 @@ (cl-defun openai-edit-create ( input instruction callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id (model "text-davinci-edit-001") diff --git a/openai-embedding.el b/openai-embedding.el index bb5a95d..fe6525b 100644 --- a/openai-embedding.el +++ b/openai-embedding.el @@ -34,7 +34,7 @@ (cl-defun openai-embedding-create ( input callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id (model "text-embedding-ada-002") diff --git a/openai-engine.el b/openai-engine.el index dfc385d..0f6c5e5 100644 --- a/openai-engine.el +++ b/openai-engine.el @@ -36,7 +36,7 @@ (cl-defun openai-engine-list ( callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Lists the currently available (non-finetuned) models, and provides basic @@ -56,7 +56,7 @@ it in." (cl-defun openai-engine-retrieve ( engine-id callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Retrieves a model instance, providing basic information about it such as the diff --git a/openai-file.el b/openai-file.el index 2bb8d7e..a7f6f0e 100644 --- a/openai-file.el +++ b/openai-file.el @@ -34,7 +34,7 @@ (cl-defun openai-file-list ( callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Return a list of files that belong to the user's organization. @@ -53,7 +53,7 @@ can overwrite the value by passing it in." (cl-defun openai-file-upload ( file purpose callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Upload a file that contain document(s) to be used across various @@ -86,7 +86,7 @@ can overwrite the value by passing it in." (cl-defun openai-file-delete ( file-id callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Delete a file. @@ -109,7 +109,7 @@ can overwrite the value by passing it in." (cl-defun openai-file-retrieve ( file-id callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Return information about a specific file. @@ -132,7 +132,7 @@ can overwrite the value by passing it in." (cl-defun openai-file-retrieve-content ( file-id callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Return the contents of the specified file diff --git a/openai-fine-tune.el b/openai-fine-tune.el index 7f7fbcf..0c359f8 100644 --- a/openai-fine-tune.el +++ b/openai-fine-tune.el @@ -33,7 +33,7 @@ (cl-defun openai-fine-tune-create ( training-file callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id (model "curie") @@ -88,7 +88,7 @@ CLASSIFICATION-POSITIVE-CLASS, CLASSIFICATION-BETAS, and SUFFIX" (cl-defun openai-fine-tune-list ( callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "List your organization's fine-tuning jobs. @@ -107,7 +107,7 @@ can overwrite the value by passing it in." (cl-defun openai-fine-tune-retrieve ( fine-tune-id callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Gets info about the fine-tune job. @@ -128,7 +128,7 @@ can overwrite the value by passing it in." (cl-defun openai-fine-tune-cancel ( fine-tune-id callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Immediately cancel a fine-tune job. @@ -149,7 +149,7 @@ can overwrite the value by passing it in." (cl-defun openai-fine-tune-list-events ( fine-tune-id callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Get fine-grained status updates for a fine-tune job. @@ -170,7 +170,7 @@ can overwrite the value by passing it in." (cl-defun openai-fine-tune-delete ( model callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Delete a fine-tuned model. You must have the Owner role in your organization. diff --git a/openai-image.el b/openai-image.el index 6b530fc..c260923 100644 --- a/openai-image.el +++ b/openai-image.el @@ -33,7 +33,7 @@ (cl-defun openai-image ( prompt callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id n @@ -67,7 +67,7 @@ for more information. Arguments here refer to N, SIZE, and RESPONSE-FORMAT." (cl-defun openai-image-edit ( image prompt callback &key - (content-type openai-content-type) + content-type (key openai-key) org-id mask @@ -105,7 +105,7 @@ RESPONSE-FORMAT." (cl-defun openai-image-variation ( image callback &key - (content-type openai-content-type) + content-type (key openai-key) org-id mask diff --git a/openai-model.el b/openai-model.el index 3aed716..866be95 100644 --- a/openai-model.el +++ b/openai-model.el @@ -31,7 +31,7 @@ (cl-defun openai-models ( callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Return models data and execute the CALLBACK. @@ -48,7 +48,7 @@ can overwrite the value by passing it in." (cl-defun openai-model ( model callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id) "Return MODEL data and execute the CALLBACK. diff --git a/openai-moderation.el b/openai-moderation.el index f2eb475..81462d7 100644 --- a/openai-moderation.el +++ b/openai-moderation.el @@ -34,7 +34,7 @@ (cl-defun openai-moderation-create ( input callback &key - (content-type openai-content-type) + (content-type "application/json") (key openai-key) org-id (model "text-moderation-latest")) diff --git a/openai.el b/openai.el index d21fbc9..5ce9f17 100644 --- a/openai.el +++ b/openai.el @@ -70,11 +70,6 @@ (funcall (plist-get (car auth-info) :secret)) (error "OpenAI API key not found in auth-source"))) -(defcustom openai-content-type "application/json" - "Default content type to make the request." - :type 'string - :group 'openai) - (defvar openai-key "" "Variable storing the openai key or a function name to retrieve it. From 5d385f57875e47d9d80b956901fb67fc52d86cb3 Mon Sep 17 00:00:00 2001 From: JenChieh Date: Thu, 6 Apr 2023 00:51:19 -0700 Subject: [PATCH 3/4] Fix checkdoc warnings --- CHANGELOG.md | 1 + openai-audio.el | 3 ++- openai-edit.el | 5 +++-- openai-embedding.el | 4 ++-- openai-engine.el | 8 ++++---- openai-fine-tune.el | 6 +++--- openai-image.el | 6 +++--- openai-moderation.el | 5 ++++- openai.el | 6 ++++-- 9 files changed, 26 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a06419..d6c55fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how > Released N/A * Add support for `Audio` requests (#9) +* Implemented `auth-source` and add arguments for `content-type` and `org-id` (#13) ## 0.1.0 > Released N/A diff --git a/openai-audio.el b/openai-audio.el index 8ae4556..f9ed72f 100644 --- a/openai-audio.el +++ b/openai-audio.el @@ -82,7 +82,8 @@ TEMPERATURE, and LANGUAGE." "Send translate audio request. Argument FILE is the audio file to translate, in one of these formats: mp3, mp4, -mpeg, mpga, m4a, wav, or webm. CALLBACK is the execuation after request is made. +mpeg, mpga, m4a, wav, or webm. CALLBACK is the execuation after request is +made. Arguments CONTENT-TYPE, KEY and ORG-ID are global options; however, you can overwrite the value by passing it in. diff --git a/openai-edit.el b/openai-edit.el index 16cf58a..a4fa403 100644 --- a/openai-edit.el +++ b/openai-edit.el @@ -41,7 +41,7 @@ temperature top-p n) - "Creates a new edit for the provided input, instruction, and parameters. + "Create a new edit for the provided input, instruction, and parameters. The INPUT is text to use as a starting point for the edit. The INSTRUCTION that tells the model how to edit the prompt. @@ -52,7 +52,8 @@ Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; however, you can overwrite the value by passing it in. 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." +for more information. Arguments here refer to MODEL, TEMPERATURE, TOP-P, and +N." (openai-request "https://api.openai.com/v1/edits" :type "POST" :headers (openai--headers content-type key org-id) diff --git a/openai-embedding.el b/openai-embedding.el index fe6525b..87abf9d 100644 --- a/openai-embedding.el +++ b/openai-embedding.el @@ -39,11 +39,11 @@ org-id (model "text-embedding-ada-002") (user openai-user)) - "Creates an embedding vector representing the input text. + "Create an embedding vector representing the input text. INPUT text to get embeddings for, encoded as a string or array of tokens. To get embeddings for multiple inputs in a single request, pass an array of -strings or array of token arrays. Each input must not exceed 8192 tokens in +strings or array of token arrays. Each input must not exceed 8192 tokens in length. The argument CALLBACK is execuated after request is made. diff --git a/openai-engine.el b/openai-engine.el index 0f6c5e5..ca9c412 100644 --- a/openai-engine.el +++ b/openai-engine.el @@ -44,8 +44,8 @@ information about each one such as the owner and availability. The argument CALLBACK is execuated after request is made. -Arguments KEY is global option; however, you can overwrite the value by passing -it in." +Arguments CONTENT-TYPE, KEY, ORG-ID and USER are global options; however, you +can overwrite the value by passing it in." (openai-request "https://api.openai.com/v1/engines" :type "GET" :headers (openai--headers content-type key org-id) @@ -66,8 +66,8 @@ The argument ENGINE-ID is the engine to use for this request. The argument CALLBACK is execuated after request is made. -Arguments KEY is global option; however, you can overwrite the value by passing -it in." +Arguments CONTENT-TYPE, KEY, ORG-ID and USER are global options; 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 (openai--headers content-type key org-id) diff --git a/openai-fine-tune.el b/openai-fine-tune.el index 0c359f8..42b704b 100644 --- a/openai-fine-tune.el +++ b/openai-fine-tune.el @@ -47,7 +47,7 @@ classification-positive-class classification-betas suffix) - "Creates a job that fine-tunes a specified model from a given dataset. + "Create a job that fine-tunes a specified model from a given dataset. Response includes details of the enqueued job including job status and the name of the fine-tuned models once complete. @@ -110,7 +110,7 @@ can overwrite the value by passing it in." (content-type "application/json") (key openai-key) org-id) - "Gets info about the fine-tune job. + "Get info about the fine-tune job. The FINE-TUNE-ID of the fine-tune job. @@ -152,7 +152,7 @@ can overwrite the value by passing it in." (content-type "application/json") (key openai-key) org-id) - "Get fine-grained status updates for a fine-tune job. + "Get fine-grained status update for a fine-tune job. The FINE-TUNE-ID of the fine-tune job to get events for. diff --git a/openai-image.el b/openai-image.el index c260923..ae17ac0 100644 --- a/openai-image.el +++ b/openai-image.el @@ -40,7 +40,7 @@ size response-format (user openai-user)) - "Creates an image given a PROMPT. + "Create an image given a PROMPT. Arguments PROMPT and CALLBACK are required for this type of request. PROMPT is either the question or instruction to OpenAI. CALLBACK is the execuation after @@ -75,7 +75,7 @@ for more information. Arguments here refer to N, SIZE, and RESPONSE-FORMAT." size response-format (user openai-user)) - "Creates an edited or extended image given an original IMAGE and a PROMPT. + "Create an edited or extended image given an original IMAGE and a PROMPT. Arguments IMAGE, PROMPT and CALLBACK are required for this type of request. PROMPT is a text description of the desired image(s). IMAGE is the image file @@ -113,7 +113,7 @@ RESPONSE-FORMAT." size response-format (user openai-user)) - "Creates a variation of a given IMAGE. + "Create a variation of a given IMAGE. Argument CALLBACK is function with data pass in, and the argument IMAGE must be a valid PNG file, less than 4MB, and square. diff --git a/openai-moderation.el b/openai-moderation.el index 81462d7..88c651a 100644 --- a/openai-moderation.el +++ b/openai-moderation.el @@ -45,7 +45,10 @@ Argument INPUT is the text to classify. The argument CALLBACK is execuated after request is made. Arguments CONTENT-TYPE, KEY, and ORG-ID are global options; however, you -can overwrite the value by passing it in." +can overwrite the value by passing it in. + +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 (openai--headers content-type key org-id) diff --git a/openai.el b/openai.el index 5ce9f17..7b9625b 100644 --- a/openai.el +++ b/openai.el @@ -99,7 +99,9 @@ otherwise return KEY." alist)) (defun openai--headers (content-type key org-id) - "Construct request headers." + "Construct request headers. + +Arguments CONTENT-TYPE, KEY, and ORG-ID are common request headers." (open--alist-omit-null `(("Content-Type" . ,content-type) ("Authorization" . ,(if (or (null key) (string-empty-p key)) @@ -108,7 +110,7 @@ otherwise return KEY." ("OpenAI-Organization" . ,org-id)))) (defun openai--json-encode (object) - "Wrapper for function `json-encode' but it removes `nil' value before + "Wrapper for function `json-encode' but it remove nil value before constructing JSON data. The argument OBJECT is an alist that can be construct to JSON data; see function From 4e830c88098a3ec6870f2e0f50b18260b81c0f9e Mon Sep 17 00:00:00 2001 From: JenChieh Date: Thu, 6 Apr 2023 01:28:28 -0700 Subject: [PATCH 4/4] Make sure resolve key --- openai.el | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/openai.el b/openai.el index 7b9625b..f418a1e 100644 --- a/openai.el +++ b/openai.el @@ -83,11 +83,11 @@ auth-source is provided for convenience.") monitor and detect abuse.") (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)))) + "If the given KEY is a function call it and return the result, otherwise +return KEY." + (cond ((functionp key) (funcall key)) + ((and (stringp key) (not (string-empty-p key))) key) + (t (user-error "[INFO] Invalid API key, please set it to the correct value: %s" key)))) (defun open--alist-omit-null (alist) "Omit null value or empty string in ALIST." @@ -102,6 +102,7 @@ otherwise return KEY." "Construct request headers. Arguments CONTENT-TYPE, KEY, and ORG-ID are common request headers." + (setq key (openai--resolve-key key)) (open--alist-omit-null `(("Content-Type" . ,content-type) ("Authorization" . ,(if (or (null key) (string-empty-p key)) @@ -115,12 +116,7 @@ constructing JSON data. The argument OBJECT is an alist that can be construct to JSON data; see function `json-encode' for the detials." - (let* ((object (cl-remove-if (lambda (pair) - (let ((value (cdr pair))) - (or (null value) ; ignore null - (and (stringp value) ; ignore empty string - (string-empty-p value))))) - object)) + (let* ((object (open--alist-omit-null object)) (encoded (json-encode object))) (openai--log "[ENCODED]: %s" encoded) encoded))