Initial commit for git.lain.church
This commit is contained in:
commit
28761a5fa2
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
systems/
|
||||
bin/
|
30
audio.lisp
Normal file
30
audio.lisp
Normal file
@ -0,0 +1,30 @@
|
||||
(in-package #:cl-openai)
|
||||
|
||||
(defun make-audio/speech-json-data (model input voice &optional response-format speed)
|
||||
(cl-json:encode-json-to-string
|
||||
(remove nil
|
||||
`(("model" . ,model)
|
||||
("input" . ,input)
|
||||
("voice" . ,voice)
|
||||
,(when response-format `("response_format" . ,response-format))
|
||||
,(when speed `("speed" . ,speed))))))
|
||||
|
||||
(defun audio/speech (model input voice &optional response-format speed)
|
||||
(let ((json-data (make-audio/speech-json-data model input voice response-format speed))
|
||||
(uri (server-path "v1/audio/speech")))
|
||||
(apply #'drakma:http-request uri
|
||||
(additional-api-args
|
||||
:method :post
|
||||
:content json-data))))
|
||||
|
||||
(defun save-audio (path response)
|
||||
(with-open-file (f path :direction :output
|
||||
:element-type '(unsigned-byte 8)
|
||||
:if-exists :supersede
|
||||
:if-does-not-exist :create)
|
||||
(loop :for byte :across response
|
||||
:do (write-byte byte f))))
|
||||
|
||||
(defun audio/speech-and-save (path model input voice &optional response-format speed)
|
||||
(let ((response (audio/speech model input voice response-format speed)))
|
||||
(save-audio path response)))
|
8
auth.lisp
Normal file
8
auth.lisp
Normal file
@ -0,0 +1,8 @@
|
||||
(in-package #:cl-openai)
|
||||
|
||||
(defparameter +openai-api-key+
|
||||
(string-trim '(#\Newline #\Return)
|
||||
(uiop:read-file-string "~/.openai-api-key.txt")))
|
||||
|
||||
(defun bearer-auth ()
|
||||
`((Authorization . ,(format nil "Bearer ~a" +openai-api-key+))))
|
41
cl-openai.asd
Normal file
41
cl-openai.asd
Normal file
@ -0,0 +1,41 @@
|
||||
(asdf:defsystem "cl-openai"
|
||||
:description "OpenAI interface for Common Lisp"
|
||||
:author "Bubblegumdrop <staticsunn@gmail.com>"
|
||||
:version "0.1.0"
|
||||
:license "WTFPL 2+"
|
||||
:defsystem-depends-on (:deploy)
|
||||
:depends-on (:cl-json
|
||||
:drakma)
|
||||
:components ((:file "package")
|
||||
(:file "auth")
|
||||
(:file "server")
|
||||
(:file "list-models")
|
||||
(:file "audio")
|
||||
(:file "prompt"))
|
||||
:in-order-to ((test-op (test-op "cl-openai/tests")))
|
||||
:build-pathname "cl-openai"
|
||||
:entry-point "cl-openai:main"
|
||||
:build-operation "deploy-op")
|
||||
|
||||
(asdf:defsystem "cl-openai/tests"
|
||||
:description "Test system for cl-openai"
|
||||
:author "Bubblegumdrop <staticsunn@gmail.com>"
|
||||
:license "WTFPL 2+"
|
||||
:depends-on ("cl-openai"
|
||||
"rove")
|
||||
:components ((:module "tests"
|
||||
:components
|
||||
(
|
||||
(:file "cl-openai")
|
||||
(:file "toolkit")
|
||||
(:module "models"
|
||||
:components
|
||||
((:file "tags"))))))
|
||||
:perform (asdf:test-op (op c) (symbol-call :rove :run c)))
|
||||
|
||||
;; https://lisp-journey.gitlab.io/blog/lisp-for-the-web-build-standalone-binaries-foreign-libraries-templates-static-assets/
|
||||
;; (deploy:define-hook (:deploy asdf) (directory)
|
||||
;; ;; Thanks again to Shinmera.
|
||||
;; (declare (ignorable directory))
|
||||
;; #+asdf (asdf:clear-source-registry)
|
||||
;; #+asdf (defun asdf:upgrade-asdf () nil))
|
11
list-models.lisp
Normal file
11
list-models.lisp
Normal file
@ -0,0 +1,11 @@
|
||||
(in-package #:cl-openai)
|
||||
|
||||
;;;; https://platform.openai.com/docs/api-reference/models/list
|
||||
;;;; https://github.com/edicl/drakma/issues/136
|
||||
(defun models/list ()
|
||||
(json-decode-http-request-to-string
|
||||
(server-path "v1/models")))
|
||||
|
||||
(defun models/retrieve (model)
|
||||
(json-decode-http-request-to-string
|
||||
(server-path (format nil "v1/models/~a" model))))
|
11
package.lisp
Normal file
11
package.lisp
Normal file
@ -0,0 +1,11 @@
|
||||
(in-package #:cl-user)
|
||||
|
||||
(defpackage #:cl-openai
|
||||
(:use #:cl)
|
||||
(:local-nicknames (:cl-json :cl-json)
|
||||
(:drakma :drakma))
|
||||
(:export #:+openai-api-key+
|
||||
#:+openai-base-url+
|
||||
#:server-path
|
||||
#:models/list
|
||||
#:models/retrieve))
|
66
prompt.lisp
Normal file
66
prompt.lisp
Normal file
@ -0,0 +1,66 @@
|
||||
(in-package #:cl-openai)
|
||||
|
||||
(defun chat/completion-messages-to-prompts (model messages)
|
||||
(cl-json:encode-json-to-string
|
||||
(remove nil
|
||||
`(("model" . ,model)
|
||||
("messages" . ,messages)))))
|
||||
|
||||
(defun prompt (model messages)
|
||||
(let ((json-data (chat/completion-messages-to-prompts model messages)))
|
||||
(json-decode-http-request-to-string
|
||||
(server-path "v1/chat/completions")
|
||||
:content json-data
|
||||
:method :post)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
#|
|
||||
(defclass chat-completion ()
|
||||
((model :initarg :model :accessor model)
|
||||
(messages :initarg :messages :accessor messages)
|
||||
(response :initarg :response :accessor response)))
|
||||
|
||||
(defun create-chat-completion (model messages)
|
||||
(make-instance 'chat-completion
|
||||
:model model
|
||||
:messages messages
|
||||
:response nil))
|
||||
|
||||
(defun add-user-message (chat-completion user-message)
|
||||
(push `(:role "user" :content ,user-message) (messages chat-completion)))
|
||||
|
||||
(defun add-assistant-response (chat-completion assistant-response)
|
||||
(setf (response chat-completion) assistant-response)
|
||||
(push `(:role "assistant" :content ,assistant-response) (messages chat-completion)))
|
||||
|
||||
(defun chat/completion-messages-to-prompts (chat-completion)
|
||||
(let ((model (model chat-completion))
|
||||
(messages (messages chat-completion)))
|
||||
(cl-json:encode-json-to-string
|
||||
(remove nil
|
||||
`(("model" . ,model)
|
||||
("messages" . ,messages))))))
|
||||
|
||||
(defun prompt (chat-completion)
|
||||
(let ((json-data (chat/completion-messages-to-prompts chat-completion)))
|
||||
(json-decode-http-request-to-string
|
||||
(server-path "v1/chat/completions")
|
||||
:content json-data
|
||||
:method :post)))
|
||||
|
||||
;; Example usage:
|
||||
(let ((chat (create-chat-completion "gpt-4o-mini"
|
||||
'((:role "system" :content "You are a helpful assistant.")))))
|
||||
(add-user-message chat "What is a LLM?")
|
||||
(let ((response (prompt chat)))
|
||||
(add-assistant-response chat response))
|
||||
(add-user-message chat "Who won the world series in 2020?")
|
||||
(let ((response (prompt chat)))
|
||||
(add-assistant-response chat response))
|
||||
(add-user-message chat "Where was it played?")
|
||||
(let ((response (prompt chat)))
|
||||
(add-assistant-response chat response))
|
||||
;; Now you can access the chat history and responses
|
||||
(format t "Chat history: ~a~%" (messages chat))
|
||||
(format t "Last assistant response: ~a~%" (response chat)))
|
||||
|#
|
27
readme.md
Normal file
27
readme.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Common Lisp OpenAI interface
|
||||
|
||||
We're currently using DRAKMA. Dexador is supported probably. Is there
|
||||
something like Clack for http clients yet?
|
||||
|
||||
Anyway, it's an OpenAI client library. You can currently:
|
||||
|
||||
- Prompt
|
||||
- Text To Speech (TTS)
|
||||
- List available models
|
||||
- Authenticate with bearer token
|
||||
|
||||
# Voices
|
||||
|
||||
Experiment with different voices:
|
||||
|
||||
- alloy
|
||||
- echo
|
||||
- fable
|
||||
- onyx
|
||||
- nova
|
||||
|
||||
# TODO
|
||||
|
||||
- Conversations API (WIP)
|
||||
- Store conversations in local DB?
|
||||
- Vision (WIP)
|
26
server.lisp
Normal file
26
server.lisp
Normal file
@ -0,0 +1,26 @@
|
||||
(in-package #:cl-openai)
|
||||
|
||||
(defvar +openai-base-uri+ "api.openai.com")
|
||||
|
||||
;; How to remove this "REMOVE NIL" instruction? I'm pretty sure I've
|
||||
;; seen it before in this macro `(when ,thing ,@(thing)) thingy.
|
||||
(defun additional-api-args (&rest args &key (content-type "application/json") &allow-other-keys)
|
||||
(remove nil
|
||||
`(:additional-headers ,(bearer-auth)
|
||||
:content-type ,content-type
|
||||
:external-format-in :utf-8
|
||||
:external-format-out :utf-8
|
||||
:force-binary t
|
||||
,@args)))
|
||||
|
||||
(defun decode-json-from-octets-string (octets)
|
||||
(cl-json:decode-json-from-string
|
||||
(flex:octets-to-string octets)))
|
||||
|
||||
(defun json-decode-http-request-to-string (uri &rest args)
|
||||
(decode-json-from-octets-string
|
||||
(apply #'drakma:http-request uri
|
||||
(apply #'additional-api-args args))))
|
||||
|
||||
(defun server-path (&optional path)
|
||||
(format nil "https://~a~:[~;/~a~]" +openai-base-uri+ path path))
|
66
systems.csv
Normal file
66
systems.csv
Normal file
@ -0,0 +1,66 @@
|
||||
deploy, ghcr.io/ocicl/deploy@sha256:e08c14d46ca07126e19a447b27d948dfa2de2ee9ed9bd9e4d106db08990bb716, deploy-20240824-f9b41f3/deploy.asd
|
||||
deploy-test, ghcr.io/ocicl/deploy@sha256:e08c14d46ca07126e19a447b27d948dfa2de2ee9ed9bd9e4d106db08990bb716, deploy-20240824-f9b41f3/deploy-test.asd
|
||||
uffi, ghcr.io/ocicl/cffi@sha256:fe1246d11c4c067daefdb143e1252e0f3e99046909185d2c920adb8323318742, cffi-20240811-32c90d4/uffi-compat/uffi.asd
|
||||
cffi, ghcr.io/ocicl/cffi@sha256:fe1246d11c4c067daefdb143e1252e0f3e99046909185d2c920adb8323318742, cffi-20240811-32c90d4/cffi.asd
|
||||
cffi-uffi-compat, ghcr.io/ocicl/cffi@sha256:fe1246d11c4c067daefdb143e1252e0f3e99046909185d2c920adb8323318742, cffi-20240811-32c90d4/cffi-uffi-compat.asd
|
||||
cffi-toolchain, ghcr.io/ocicl/cffi@sha256:fe1246d11c4c067daefdb143e1252e0f3e99046909185d2c920adb8323318742, cffi-20240811-32c90d4/cffi-toolchain.asd
|
||||
cffi-tests, ghcr.io/ocicl/cffi@sha256:fe1246d11c4c067daefdb143e1252e0f3e99046909185d2c920adb8323318742, cffi-20240811-32c90d4/cffi-tests.asd
|
||||
cffi-libffi, ghcr.io/ocicl/cffi@sha256:fe1246d11c4c067daefdb143e1252e0f3e99046909185d2c920adb8323318742, cffi-20240811-32c90d4/cffi-libffi.asd
|
||||
cffi-grovel, ghcr.io/ocicl/cffi@sha256:fe1246d11c4c067daefdb143e1252e0f3e99046909185d2c920adb8323318742, cffi-20240811-32c90d4/cffi-grovel.asd
|
||||
cffi-examples, ghcr.io/ocicl/cffi@sha256:fe1246d11c4c067daefdb143e1252e0f3e99046909185d2c920adb8323318742, cffi-20240811-32c90d4/cffi-examples.asd
|
||||
alexandria, ghcr.io/ocicl/alexandria@sha256:e433c2e076ed3bcf8641b97b00192680db2201d305efac9293539dee88c7fbf7, alexandria-20240503-8514d8e/alexandria.asd
|
||||
trivial-features, ghcr.io/ocicl/trivial-features@sha256:e278e24d39060fc7d3715531b16ddd9ab7f93f22ade53e436c0b86dbae3aa065, trivial-features-1.0/trivial-features.asd
|
||||
trivial-features-tests, ghcr.io/ocicl/trivial-features@sha256:e278e24d39060fc7d3715531b16ddd9ab7f93f22ade53e436c0b86dbae3aa065, trivial-features-1.0/trivial-features-tests.asd
|
||||
babel, ghcr.io/ocicl/babel@sha256:b505136744213e6b953913780a4b647da40e83818a10f41004846863b5037810, babel-20240606-23c1440/babel.asd
|
||||
babel-tests, ghcr.io/ocicl/babel@sha256:b505136744213e6b953913780a4b647da40e83818a10f41004846863b5037810, babel-20240606-23c1440/babel-tests.asd
|
||||
babel-streams, ghcr.io/ocicl/babel@sha256:b505136744213e6b953913780a4b647da40e83818a10f41004846863b5037810, babel-20240606-23c1440/babel-streams.asd
|
||||
sha3, ghcr.io/ocicl/sha3@sha256:556fcc3d3fcba08185de62f7bb090fa233af4f8c68a4d7401992a32e03fb9a70, sha3-20240503-a4baa05/sha3.asd
|
||||
multilang-documentation-utils, ghcr.io/ocicl/documentation-utils@sha256:b2a1b3f3bcd1a738af85ae2b0168d408c177661eab6d6bbebb254e394d983f54, documentation-utils-20230511-98630dd/multilang-documentation-utils.asd
|
||||
documentation-utils, ghcr.io/ocicl/documentation-utils@sha256:b2a1b3f3bcd1a738af85ae2b0168d408c177661eab6d6bbebb254e394d983f54, documentation-utils-20230511-98630dd/documentation-utils.asd
|
||||
trivial-indent, ghcr.io/ocicl/trivial-indent@sha256:f1ae624eb2ca37133912e9920587488ea6e2f1d6419ba4a1776e63c2bdff0fd9, trivial-indent-20240503-b5d490f/trivial-indent.asd
|
||||
cl-json, ghcr.io/ocicl/cl-json@sha256:54c4d00901c2c5b0ea9da11eed8f0104fc5819e1ed9a29d1f3364503a720a868, cl-json-20240907-994dd38/cl-json.asd
|
||||
drakma, ghcr.io/ocicl/drakma@sha256:9b9883fa280fae200d34951395011df3765ed73cfcebf551a44ff246a756b576, drakma-2.0.10/drakma.asd
|
||||
drakma-test, ghcr.io/ocicl/drakma@sha256:9b9883fa280fae200d34951395011df3765ed73cfcebf551a44ff246a756b576, drakma-2.0.10/drakma-test.asd
|
||||
puri, ghcr.io/ocicl/puri@sha256:f6aa091a4d8eb5bdc91466daddc90dadd5b0fcf247370df237a223dc0588f29d, puri-4bbab9/puri.asd
|
||||
cl-base64, ghcr.io/ocicl/cl-base64@sha256:702412dbc1ed825e275fb23e22c29527eb786bf644e47c7621bf0dbb34e17c7e, cl-base64-20240503-80496b7/cl-base64.asd
|
||||
cl-base64-tests, ghcr.io/ocicl/cl-base64@sha256:702412dbc1ed825e275fb23e22c29527eb786bf644e47c7621bf0dbb34e17c7e, cl-base64-20240503-80496b7/cl-base64-tests.asd
|
||||
chunga, ghcr.io/ocicl/chunga@sha256:b3b986951e2fbd881f038194563c74b235705c4259e9206902e314fdd3933cb3, chunga-20240503-fab9bd3/chunga.asd
|
||||
trivial-gray-streams, ghcr.io/ocicl/trivial-gray-streams@sha256:d0cc9fb73876100ee60647524f98b5b22566fa1c3a985d79548b0d2b228f8b51, trivial-gray-streams-20240503-a7ead68/trivial-gray-streams.asd
|
||||
trivial-gray-streams-test, ghcr.io/ocicl/trivial-gray-streams@sha256:d0cc9fb73876100ee60647524f98b5b22566fa1c3a985d79548b0d2b228f8b51, trivial-gray-streams-20240503-a7ead68/trivial-gray-streams-test.asd
|
||||
flexi-streams, ghcr.io/ocicl/flexi-streams@sha256:95005b06a8a95c82780c75ead53810bcb273d07fcf5fe76b527f1856df9923f2, flexi-streams-20240503-4951d57/flexi-streams.asd
|
||||
flexi-streams-test, ghcr.io/ocicl/flexi-streams@sha256:95005b06a8a95c82780c75ead53810bcb273d07fcf5fe76b527f1856df9923f2, flexi-streams-20240503-4951d57/flexi-streams-test.asd
|
||||
cl-ppcre, ghcr.io/ocicl/cl-ppcre@sha256:584907e0683621973579f397115945ef73e9d5b7afa77fae7cacecb3ad272f89, cl-ppcre-20240503-80fb19d/cl-ppcre.asd
|
||||
cl-ppcre-unicode, ghcr.io/ocicl/cl-ppcre@sha256:584907e0683621973579f397115945ef73e9d5b7afa77fae7cacecb3ad272f89, cl-ppcre-20240503-80fb19d/cl-ppcre-unicode.asd
|
||||
chipz, ghcr.io/ocicl/chipz@sha256:fae0fae0c93199ba57c92d9102245a55c8a2c12af4442e0b141d5670bef4f2f0, chipz-20240503-6f80368/chipz.asd
|
||||
usocket, ghcr.io/ocicl/usocket@sha256:015b8cdb91cdc25aefd08054e8ede6a5d054d10bc87916904cf0d0670b3f4a68, usocket-0.8.8/usocket.asd
|
||||
usocket-test, ghcr.io/ocicl/usocket@sha256:015b8cdb91cdc25aefd08054e8ede6a5d054d10bc87916904cf0d0670b3f4a68, usocket-0.8.8/usocket-test.asd
|
||||
usocket-server, ghcr.io/ocicl/usocket@sha256:015b8cdb91cdc25aefd08054e8ede6a5d054d10bc87916904cf0d0670b3f4a68, usocket-0.8.8/usocket-server.asd
|
||||
split-sequence, ghcr.io/ocicl/split-sequence@sha256:3a37662eedd99c42995587b9d443c4631f25c4c523091bcca31a27b880585b8e, split-sequence-2.0.1/split-sequence.asd
|
||||
cl_plus_ssl.test, ghcr.io/ocicl/cl_plus_ssl@sha256:acb61e8e4d3efcb23f4d86405ad22fdca2d685268c664735c962646a09706ab6, cl-plus-ssl-20240505-17d5cdd/cl+ssl.test.asd
|
||||
cl_plus_ssl, ghcr.io/ocicl/cl_plus_ssl@sha256:acb61e8e4d3efcb23f4d86405ad22fdca2d685268c664735c962646a09706ab6, cl-plus-ssl-20240505-17d5cdd/cl+ssl.asd
|
||||
bordeaux-threads, ghcr.io/ocicl/bordeaux-threads@sha256:bbb32afe3987e102149cc019f4f7366a05f7a37d1503f5f86a27745e671c320a, bordeaux-threads-0.9.4/bordeaux-threads.asd
|
||||
global-vars, ghcr.io/ocicl/global-vars@sha256:d197e871c9c9abc4bf31b1186f5fd28f0ca848ebe2e9dddf2d82ecee01dbdc65, global-vars-20240503-c749f32/global-vars.asd
|
||||
global-vars-test, ghcr.io/ocicl/global-vars@sha256:d197e871c9c9abc4bf31b1186f5fd28f0ca848ebe2e9dddf2d82ecee01dbdc65, global-vars-20240503-c749f32/global-vars-test.asd
|
||||
trivial-garbage, ghcr.io/ocicl/trivial-garbage@sha256:a85dcf4110ad60ae9f6c70970acceb2bf12402ce5326891643d35506059afb1d, trivial-garbage-20240503-3474f64/trivial-garbage.asd
|
||||
dexador, ghcr.io/ocicl/dexador@sha256:538d1a96f5c090d4793fde903f343790eef50c3e9c64413ebd3ee77799221faf, dexador-20240711-d2717a4/dexador.asd
|
||||
dexador-usocket, ghcr.io/ocicl/dexador@sha256:538d1a96f5c090d4793fde903f343790eef50c3e9c64413ebd3ee77799221faf, dexador-20240711-d2717a4/dexador-usocket.asd
|
||||
dexador-test, ghcr.io/ocicl/dexador@sha256:538d1a96f5c090d4793fde903f343790eef50c3e9c64413ebd3ee77799221faf, dexador-20240711-d2717a4/dexador-test.asd
|
||||
fast-http, ghcr.io/ocicl/fast-http@sha256:44e98b5239c0ded4921dc0b04ae272cff00583d93797a657543db782979eb50c, fast-http-20240503-2232fc9/fast-http.asd
|
||||
fast-http-test, ghcr.io/ocicl/fast-http@sha256:44e98b5239c0ded4921dc0b04ae272cff00583d93797a657543db782979eb50c, fast-http-20240503-2232fc9/fast-http-test.asd
|
||||
cl-utilities, ghcr.io/ocicl/cl-utilities@sha256:e5e0676a4e0627332a0fe64d56ed4f1890f0466d715a11e8d95561b4c2baa38f, cl-utilities-20240503-6b4de39/cl-utilities.asd
|
||||
proc-parse, ghcr.io/ocicl/proc-parse@sha256:0880f9acb35eb9efbe1f77a981e36e84e8f29a22b14f680c7686eb381fa87bd6, proc-parse-20240503-3afe2b7/proc-parse.asd
|
||||
proc-parse-test, ghcr.io/ocicl/proc-parse@sha256:0880f9acb35eb9efbe1f77a981e36e84e8f29a22b14f680c7686eb381fa87bd6, proc-parse-20240503-3afe2b7/proc-parse-test.asd
|
||||
xsubseq, ghcr.io/ocicl/xsubseq@sha256:7605d14f7cc7fd2f63190ad81fe7bd65daef105d6728ee6bb59baef49da2aa51, xsubseq-20240503-5ce430b/xsubseq.asd
|
||||
xsubseq-test, ghcr.io/ocicl/xsubseq@sha256:7605d14f7cc7fd2f63190ad81fe7bd65daef105d6728ee6bb59baef49da2aa51, xsubseq-20240503-5ce430b/xsubseq-test.asd
|
||||
smart-buffer, ghcr.io/ocicl/smart-buffer@sha256:b8d194f5881c415fd7f0099b88dfd9e2158405c2966de15c716b155a364bf0f7, smart-buffer-20240503-619759d/smart-buffer.asd
|
||||
smart-buffer-test, ghcr.io/ocicl/smart-buffer@sha256:b8d194f5881c415fd7f0099b88dfd9e2158405c2966de15c716b155a364bf0f7, smart-buffer-20240503-619759d/smart-buffer-test.asd
|
||||
quri, ghcr.io/ocicl/quri@sha256:e2559f91600e4895e115b42dde88fcaac3677f051335d73431888dcf7af99f95, quri-0.7.0/quri.asd
|
||||
quri-test, ghcr.io/ocicl/quri@sha256:e2559f91600e4895e115b42dde88fcaac3677f051335d73431888dcf7af99f95, quri-0.7.0/quri-test.asd
|
||||
fast-io, ghcr.io/ocicl/fast-io@sha256:7ba35a0eddf00b3c25656b3c5a93e95460f6cb8f807afbc04ef4f50076d95aff, fast-io-20240503-a4c5ad6/fast-io.asd
|
||||
fast-io-test, ghcr.io/ocicl/fast-io@sha256:7ba35a0eddf00b3c25656b3c5a93e95460f6cb8f807afbc04ef4f50076d95aff, fast-io-20240503-a4c5ad6/fast-io-test.asd
|
||||
static-vectors, ghcr.io/ocicl/static-vectors@sha256:53e11e5689c3dbea6a3017760b55f052588003d774913539930ddc4330a69970, static-vectors-20240624-3d9d89b/static-vectors.asd
|
||||
cl-cookie, ghcr.io/ocicl/cl-cookie@sha256:85cd8aa7f1379d041fae9530609cd68d5214fb978bc87faa7fd9ad5ad7e08a83, cl-cookie-20240703-6bcb74a/cl-cookie.asd
|
||||
cl-cookie-test, ghcr.io/ocicl/cl-cookie@sha256:85cd8aa7f1379d041fae9530609cd68d5214fb978bc87faa7fd9ad5ad7e08a83, cl-cookie-20240703-6bcb74a/cl-cookie-test.asd
|
||||
local-time, ghcr.io/ocicl/local-time@sha256:f6b11fc15cf92adb87a847d3af918ada947c2648660e74133db9183e7da0db70, local-time-20240817-5a09552/local-time.asd
|
||||
cl-postgres_plus_local-time, ghcr.io/ocicl/local-time@sha256:f6b11fc15cf92adb87a847d3af918ada947c2648660e74133db9183e7da0db70, local-time-20240817-5a09552/cl-postgres+local-time.asd
|
||||
trivial-mimes, ghcr.io/ocicl/trivial-mimes@sha256:b33ae6d62bcd666d046e4ae1625b8e8b46a548fe2c4f18b4899ce1ecf581c17b, trivial-mimes-20240503-1a80193/trivial-mimes.asd
|
|
42
vision.lisp
Normal file
42
vision.lisp
Normal file
@ -0,0 +1,42 @@
|
||||
(in-package #:cl-openai)
|
||||
|
||||
#|
|
||||
payload = {
|
||||
"model": "gpt-4o-mini",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "What’s in this image?"
|
||||
},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:image/jpeg;base64,{base64_image}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"max_tokens": 300
|
||||
}
|
||||
|
||||
The Chat Completions API, unlike the Assistants API, is not stateful. That means you have to manage the messages (including images) you pass to the model yourself. If you want to pass the same image to the model multiple times, you will have to pass the image each time you make a request to the API.
|
||||
|
||||
For long running conversations, we suggest passing images via URL's instead of base64. The latency of the model can also be improved by downsizing your images ahead of time to be less than the maximum size they are expected them to be. For low res mode, we expect a 512px x 512px image. For high res mode, the short side of the image should be less than 768px and the long side should be less than 2,000px.
|
||||
|#
|
||||
|
||||
(defun read-binary-file (filespec)
|
||||
(with-open-file (stream filespec
|
||||
:element-type '(unsigned-byte 8))
|
||||
(let ((buffer (make-array (file-length stream)
|
||||
:element-type '(unsigned-byte 8))))
|
||||
(read-sequence buffer stream) buffer)))
|
||||
|
||||
(defun base64-stream-string (filespec)
|
||||
(let ((binary-data (read-binary-file filespec)))
|
||||
(with-output-to-string (s)
|
||||
(base64:usb8-array-to-base64-stream binary-data s))))
|
||||
|
20
whisper.lisp
Normal file
20
whisper.lisp
Normal file
@ -0,0 +1,20 @@
|
||||
(in-package #:cl-openai)
|
||||
|
||||
#|
|
||||
curl --request POST \
|
||||
--url https://api.openai.com/v1/audio/transcriptions \
|
||||
--header "Authorization: Bearer $OPENAI_API_KEY" \
|
||||
--header 'Content-Type: multipart/form-data' \
|
||||
--form file=@/path/to/file/audio.mp3 \
|
||||
--form model=whisper-1
|
||||
|#
|
||||
|
||||
(defun transcribe-audio (file &optional (model "whisper-1"))
|
||||
(let ((args `(:method :post
|
||||
:content-type "multipart/form-data"
|
||||
:parameters (("file" . ,file)
|
||||
("model" . ,model))))
|
||||
(uri (server-path "v1/audio/transcriptions")))
|
||||
(decode-json-from-octets-string
|
||||
(apply #'drakma:http-request uri
|
||||
(apply #'additional-api-args args)))))
|
Loading…
Reference in New Issue
Block a user