diff options
| author | Arun Isaac | 2026-04-03 19:49:52 +0100 |
|---|---|---|
| committer | Arun Isaac | 2026-04-03 19:49:52 +0100 |
| commit | be549e815698cf633354447d41bec368b121b523 (patch) | |
| tree | c60d2bf6e341f85ffa0d2c734cc06793c16eb508 /kaakaa/openai.scm | |
| download | kaagum-be549e815698cf633354447d41bec368b121b523.tar.gz kaagum-be549e815698cf633354447d41bec368b121b523.tar.lz kaagum-be549e815698cf633354447d41bec368b121b523.zip | |
Initial commit
Diffstat (limited to 'kaakaa/openai.scm')
| -rw-r--r-- | kaakaa/openai.scm | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/kaakaa/openai.scm b/kaakaa/openai.scm new file mode 100644 index 0000000..08a7254 --- /dev/null +++ b/kaakaa/openai.scm @@ -0,0 +1,79 @@ +;;; kaakaa --- Tiny, security-focused AI agent in Guile +;;; Copyright © 2026 Arun Isaac <arunisaac@systemreboot.net> +;;; +;;; This file is part of kaakaa. +;;; +;;; kaakaa 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. +;;; +;;; kaakaa 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 kaakaa. If not, see <https://www.gnu.org/licenses/>. + +(define-module (kaakaa openai) + #:use-module (rnrs conditions) + #:use-module (rnrs exceptions) + #:use-module (srfi srfi-11) + #:use-module (web client) + #:use-module (web http) + #:use-module (web response) + #:use-module (json) + #:export (get-api-key + openai-query)) + +;; TODO: URIs must not be operated on using string operations. Replace with a +;; more principled implementation involving (web uri). +(define (uri-join base uri) + (string-append base uri)) + +(define* (json-post url #:key headers json) + "Send a POST request to @var{url} with @var{json} body and additional +@var{headers}. The @samp{Content-Type} header is set to @samp{application/json} +and need not be specified in @var{headers}. Return JSON response." + (let-values (((response body) + (http-post url + #:headers `((content-type application/json) + ,@headers) + #:body (scm->json-string json) + #:streaming? #t))) + ;; Guile does not consider application/json responses as textual, and does + ;; not automatically set the port encoding to UTF-8. + (set-port-encoding! body "UTF-8") + (case (quotient (response-code response) + 100) + ((2) (json->scm body)) + ((4) + (raise-exception + (condition (make-violation) + (make-message-condition + (string-append "JSON API request failed with client error code " + (number->string (response-code response))))))) + (else + (raise-exception + (condition (make-error) + (make-message-condition + (string-append "JSON API request failed with code " + (number->string (response-code response)))))))))) + +;; Declare the Authorization header as opaque so that Guile doesn't try to mess +;; with it. +(declare-opaque-header! "Authorization") + +(define (openai-query base-uri api-key model messages tools) + "Send a request to the OpenAI completions API and return the JSON response. +@var{base-uri} is the base URI of the OpenAI-compatible service. @var{api-key} +is the API key for authentication. @var{model} is a supported LLM model. +@var{messages} and @var{tools} are respectively lists of JSON messages and tools +compatible with the OpenAI API specification." + (json-post (uri-join base-uri "/api/v1/chat/completions") + #:headers `((authorization + . ,(string-append "Bearer " api-key))) + #:json `(("model" . ,model) + ("messages" . ,(list->vector messages)) + ("tools" . ,(list->vector tools))))) |
