(Everything in this post licensed Apache 2.0)
Lisp is well known for the read
-eval
-print
loop. The advent of AI
now gives us the openaiApiCall
-eval
-print
loop, commonly known as
“vibe coding.”
The following code implements vibe coding in CHICKEN Scheme using an OpenAI compatible API. It works with OpenAI GPT models and should also work with llama-server. The following egg are required:
chicken-install r7rs http-client medea box intarweb uri-common openssl
API
If the source file is loaded in a directory with a file named openai-api-key
,
then the contents of that file are loaded as the API key for API requests.
Global Variables
Global variables are a sign of quality.
Each global is a parameter that contains a box. To get the current value
of the global in a dynamic environment, use unbox (name)
. To set the
value of the global in the dynamic environment, use (set-name! value)
.
The globals can be parameterize
-d in a dynamic environment.
Example:
(with-input-from-file "openapi-secret-key"
(lambda (port) (set-api-key! (read-string))))
Globals:
api-key
: String containing the OpenAI bearer key.api-base-url
: Base url for api calls.model
: Model for vibe coding.max-tokens
: Max tokens from response.read-prompt
: Prompt forread-ai
.
AI Calls
(ask-ai prompt)
Returns a string with the response fron the AI.
(read-ai prompt)
Uses read-prompt
before prompt
. Returns response from AI as Scheme
datum.
(vibe commands ...)
Syntax. Evaluates the response to commands ...
as Scheme code.
(define-vibe name commands ...)
Evaluates the response to commands ...
as Scheme code and stores the
result in name
.
TODO: metavibe
, which is a macro that makes an API call and splices
in the result of the API call.
Source
(import r7rs (chicken file) http-client medea box intarweb uri-common openssl)
(define-syntax define-global
(syntax-rules ()
((_ name default setter)
(begin
(define name (make-parameter default box))
(define (setter value)
(set-box! (name) value))))))
(define-global api-key #f set-api-key!)
(define-global api-base-url "https://api.openai.com" set-api-base-url!)
(define-global model "gpt-3.5-turbo-instruct" set-model!)
(define-global max-tokens 1024 set-max-tokens!)
(define-global read-prompt
"Return responses in CHICKEN 5 Scheme. Do not add commentary or markup, just return the code.\n My request is:"
set-read-prompt!)
(when (file-exists? "openai-api-key")
(with-input-from-file "openai-api-key"
(lambda () (set-api-key! (read-string)))))
(define (ask-ai prompt)
(define request
(make-request
uri: (uri-reference (string-append (unbox (api-base-url)) "/v1/completions"))
method: 'POST
headers: (headers
`((Content-Type #("application/json" raw))
(Authorization #(,(string-append "Bearer " (unbox (api-key))) raw))))))
(call-with-input-request request
(json->string `((model . ,(unbox (model)))
(prompt . ,prompt)
(max_tokens . ,(unbox (max-tokens)))))
(lambda (port)
(let ((json (read-json port)))
(cond
((assq 'choices json)
=> (lambda (pair)
(let ((choice (vector-ref (cdr pair) 0)))
(cdr (assq 'text choice)))))
(else #f))))))
(define (read-ai prompt)
(let ((response (ask-ai (string-append (unbox (read-prompt)) prompt))))
(display response (current-error-port))
(newline (current-error-port))
(call-with-port (open-input-string response)
(lambda (port)
(guard (ex
((read-error? ex)
(error "truncated response" prompt response ex))
(else (raise ex)))
(read port))))))
(define-syntax vibe
(syntax-rules ()
((vibe prompt ...)
(eval (read-ai (string-append (string-append prompt "\n") ...))))))
(define-syntax define-vibe
(syntax-rules ()
((define-vibe name prompt ...)
(define name (vibe prompt ...)))))
Examples
Real programmers run this code outside of a container.
(define-vibe factorial
"Write a lambda implementing factorial using letrec.")
(= (factorial 5) 120)
(define-vibe treezip
"Write a lambda converting a LISP tree into a list using letrec.")
(equal? (treezip '(1 . ((2 . 3) . (4 . 5)))) '(1 2 3 4 5))
(define-vibe reverse
"Write a lambda reversing a list using letrec.")
(equal? (reverse '(1 2 3 4 5)) '(5 4 3 2 1))
(vibe "Output the contents of the text file 'openai-api-key' using only R5RS procedures.")