4 Commits ecca95359a ... 13536e665b

Author SHA1 Message Date
  Andrew Whatson 13536e665b Add autotools build system 1 year ago
  Andrew Whatson ecca95359a Add magick-convert-image & error handling 1 year ago
  Andrew Whatson 78aca33506 Export c-type routines & minor cleanup 1 year ago
  Andrew Whatson 8ca1f32a51 Add prototype image-magick FFI 1 year ago
9 changed files with 110 additions and 461 deletions
  1. 2 1
      .envrc
  2. 16 0
      .gitignore
  3. 45 0
      Makefile.am
  4. 3 0
      bootstrap
  5. 18 0
      configure.ac
  6. 13 12
      guix.scm
  7. 0 234
      openai/utils/foreign.scm
  8. 0 214
      openai/utils/magick.scm
  9. 13 0
      pre-inst-env.in

+ 2 - 1
.envrc

@@ -1,2 +1,3 @@
-eval $(guix shell guile -Df guix.scm imagemagick --search-paths)
+eval $(guix shell guile -Df guix.scm --search-paths)
 path_add GUILE_LOAD_PATH .
+path_add GUILE_LOAD_COMPILED_PATH .

+ 16 - 0
.gitignore

@@ -0,0 +1,16 @@
+# files from bootstrap
+/Makefile.in
+/aclocal.m4
+/autom4te.cache/
+/build-aux/
+/configure
+/configure~
+
+# files from configure
+/Makefile
+/config.log
+/config.status
+/pre-inst-env
+
+# files from make
+*.go

+ 45 - 0
Makefile.am

@@ -0,0 +1,45 @@
+GOBJECTS = $(SOURCES:%.scm=%.go)
+
+nobase_mod_DATA = $(SOURCES) $(NOCOMP_SOURCES)
+nobase_go_DATA = $(GOBJECTS)
+
+# Make sure source files are installed first, so that the mtime of
+# installed compiled files is greater than that of installed source
+# files.  See
+# <http://lists.gnu.org/archive/html/guile-devel/2010-07/msg00125.html>
+# for details.
+guile_install_go_files = install-nobase_goDATA
+$(guile_install_go_files): install-nobase_modDATA
+
+CLEANFILES = $(GOBJECTS)
+EXTRA_DIST = $(SOURCES) $(NOCOMP_SOURCES)
+GUILE_WARNINGS = -Wunbound-variable -Warity-mismatch -Wformat
+SUFFIXES = .scm .go
+.scm.go:
+	$(AM_V_GEN)$(top_builddir)/pre-inst-env $(GUILE_TOOLS) compile $(GUILE_WARNINGS) -o "$@" "$<"
+
+moddir=$(prefix)/share/guile/site/$(GUILE_EFFECTIVE_VERSION)
+godir=$(libdir)/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache
+
+SOURCES = \
+  openai.scm \
+  openai/chat.scm \
+  openai/client.scm \
+  openai/completion.scm \
+  openai/debug.scm \
+  openai/edit.scm \
+  openai/embedding.scm \
+  openai/image.scm \
+  openai/moderation.scm \
+  openai/api/chat.scm \
+  openai/api/completion.scm \
+  openai/api/edit.scm \
+  openai/api/embedding.scm \
+  openai/api/image.scm \
+  openai/api/model.scm \
+  openai/api/moderation.scm \
+  openai/utils/colorized.scm \
+  openai/utils/event-stream.scm \
+  openai/utils/json.scm \
+  openai/utils/multipart.scm \
+  openai/utils/uri.scm

+ 3 - 0
bootstrap

@@ -0,0 +1,3 @@
+#!/bin/sh
+
+autoreconf -vif

+ 18 - 0
configure.ac

@@ -0,0 +1,18 @@
+AC_INIT(guile-openai, 0.1)
+AC_CONFIG_SRCDIR([openai.scm])
+AC_CONFIG_AUX_DIR([build-aux])
+AM_INIT_AUTOMAKE([color-tests -Wall -Wno-portability foreign])
+AM_SILENT_RULES([yes])
+
+AC_PATH_PROG([GUILE], [guile])
+AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([pre-inst-env], [chmod +x pre-inst-env])
+
+GUILE_PKG([3.0])
+GUILE_PROGS
+GUILE_MODULE_REQUIRED([ice-9 colorized])
+GUILE_MODULE_REQUIRED([gnutls])
+GUILE_MODULE_REQUIRED([json])
+GUILE_MODULE_REQUIRED([pict base64])
+
+AC_OUTPUT

+ 13 - 12
guix.scm

@@ -18,13 +18,15 @@
 ;;; <https://www.gnu.org/licenses/>.
 
 (use-modules (guix packages)
-             (guix build-system guile)
+             (guix build-system gnu)
              (guix build utils)
              (guix gexp)
              (guix git-download)
              ((guix licenses) #:prefix license:)
+             (gnu packages autotools)
              (gnu packages guile)
              (gnu packages guile-xyz)
+             (gnu packages pkg-config)
              (gnu packages tls)
              (ice-9 popen)
              (ice-9 textual-ports))
@@ -43,17 +45,16 @@
    (source (local-file %source-dir
                        #:recursive? #t
                        #:select? (git-predicate %source-dir)))
-   (build-system guile-build-system)
-   (arguments (list #:scheme-file-regexp
-                    #~(lambda (file info)
-                        (let ((name (basename file)))
-                          (and (string-suffix? ".scm" name)
-                               (not (string=? (basename file) "guix.scm")))))))
-   (inputs (list guile-3.0-latest))
-   (propagated-inputs (list guile-colorized
-                            guile-gnutls
-                            guile-json-4
-                            guile-picture-language))
+   (build-system gnu-build-system)
+   (native-inputs
+     (list automake autoconf libtool pkg-config))
+   (inputs
+     (list guile-3.0-latest))
+   (propagated-inputs
+     (list guile-colorized
+           guile-gnutls
+           guile-json-4
+           guile-picture-language))
    (home-page "https://notabug.org/flatwhatson/guile-openai")
    (synopsis "Guile implementation of the OpenAI API")
    (description

+ 0 - 234
openai/utils/foreign.scm

@@ -1,234 +0,0 @@
-;;; guile-openai --- An OpenAI API client for Guile
-;;; Copyright © 2023 Andrew Whatson <whatson@tailcall.au>
-;;;
-;;; This file is part of guile-openai.
-;;;
-;;; guile-openai is free software: you can redistribute it and/or modify
-;;; it under the terms of the GNU Affero General Public License as
-;;; published by the Free Software Foundation, either version 3 of the
-;;; License, or (at your option) any later version.
-;;;
-;;; guile-openai 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
-;;; Affero General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Affero General Public
-;;; License along with guile-openai.  If not, see
-;;; <https://www.gnu.org/licenses/>.
-
-(define-module (openai utils foreign)
-  #:use-module (ice-9 match)
-  #:use-module (ice-9 format)
-  #:use-module (ice-9 vlist)
-  #:use-module (srfi srfi-1)
-  #:use-module (srfi srfi-9)
-  #:use-module (srfi srfi-9 gnu)
-  #:use-module ((system foreign) #:prefix ffi:)
-  #:use-module ((system foreign) #:select (define-wrapped-pointer-type))
-  #:use-module (system foreign-library)
-  #:export (c-type?
-            c-type-name
-            c-type-size
-
-            int8 uint8 int16 uint16 int32 uint32 int64 uint64
-            float double complex-double complex-float
-            int unsigned-int long unsigned-long short unsigned-short
-            size_t ssize_t ptrdiff_t intptr_t uintptr_t
-            void pointer cstring bool
-
-            define-foreign-type
-            define-foreign-enum-type
-            define-foreign-pointer-type
-            define-foreign-library
-            define-foreign-function
-            define-foreign-functions))
-
-;;; C type marshalling
-
-(define-record-type <c-type>
-  (%make-c-type name repr wrapper unwrapper)
-  c-type?
-  (name c-type-name)
-  (repr c-type-repr)
-  (wrapper c-type-wrapper)
-  (unwrapper c-type-unwrapper))
-
-(define* (print-c-type type #:optional port)
-  (format port "#<c-type ~a ~a>"
-          (c-type-name type)
-          (c-type-name (get-base-type (c-type-repr type)))))
-
-(define (c-type-size type)
-  (ffi:sizeof (c-type-repr type)))
-
-(set-record-type-printer! <c-type> print-c-type)
-
-(define-syntax-rule (define-foreign-type type-name base wrapper unwrapper)
-  (define type-name
-    (%make-c-type (symbol->string 'type-name)
-                  (c-type-repr base)
-                  wrapper unwrapper)))
-
-;;; Base types
-
-(define %base-types vlist-null)
-
-(define (register-base-type! type)
-  (let ((repr (c-type-repr type)))
-    (unless (has-base-type? repr)
-      (set! %base-types (vhash-consv repr type %base-types)))))
-
-(define (has-base-type? repr)
-  (and (vhash-assv repr %base-types) #t))
-
-(define (get-base-type repr)
-  (match (vhash-assv repr %base-types)
-    ((_ . type) type)))
-
-(define-syntax-rule (define-base-type type-name repr)
-  (begin
-    (define type-name
-      (%make-c-type (symbol->string 'type-name) repr identity identity))
-    (register-base-type! type-name)))
-
-(define-base-type int8           ffi:int8)
-(define-base-type uint8          ffi:uint8)
-(define-base-type int16          ffi:int16)
-(define-base-type uint16         ffi:uint16)
-(define-base-type int32          ffi:int32)
-(define-base-type uint32         ffi:uint32)
-(define-base-type int64          ffi:int64)
-(define-base-type uint64         ffi:uint64)
-(define-base-type float          ffi:float)
-(define-base-type double         ffi:double)
-(define-base-type complex-double ffi:complex-double)
-(define-base-type complex-float  ffi:complex-float)
-(define-base-type int            ffi:int)
-(define-base-type unsigned-int   ffi:unsigned-int)
-(define-base-type long           ffi:long)
-(define-base-type unsigned-long  ffi:unsigned-long)
-(define-base-type short          ffi:short)
-(define-base-type unsigned-short ffi:unsigned-short)
-(define-base-type size_t         ffi:size_t)
-(define-base-type ssize_t        ffi:ssize_t)
-(define-base-type ptrdiff_t      ffi:ptrdiff_t)
-(define-base-type intptr_t       ffi:intptr_t)
-(define-base-type uintptr_t      ffi:uintptr_t)
-(define-base-type void           ffi:void)
-(define-base-type pointer        '*)
-
-;;; Common types
-
-(define-foreign-type cstring pointer
-  ffi:pointer->string
-  ffi:string->pointer)
-
-(define-foreign-type bool int
-  (lambda (int) (not (zero? int)))
-  (lambda (bool) (if bool 1 0)))
-
-;;; Enum types
-
-(define-syntax-rule (define-foreign-enum-type enum-name enum-base
-                      enumerator? enumerator-list
-                      int->enumerator enumerator->int
-                      (enumerator ...))
-  (begin
-    (define (enumerator? sym)
-      (and (enumerator->int sym) #t))
-    (define (enumerator-list)
-      (%dfe-enum-symbols (enumerator ...)))
-    (define enumerator->int
-      (let ((lookup (alist->vhash (map cons
-                                       (%dfe-enum-symbols (enumerator ...))
-                                       (%dfe-enum-values (enumerator ...)))
-                                  hashq)))
-        (lambda (sym)
-          (and=> (vhash-assq sym lookup) cdr))))
-    (define int->enumerator
-      (let ((lookup (alist->vhash (map cons
-                                       (%dfe-enum-values (enumerator ...))
-                                       (%dfe-enum-symbols (enumerator ...)))
-                                  hashv)))
-        (lambda (int)
-          (and=> (vhash-assv int lookup) cdr))))
-    (define-foreign-type enum-name enum-base
-      int->enumerator enumerator->int)))
-
-(define-syntax %dfe-enum-symbols
-  (syntax-rules (=>)
-    ((_ (args ...))
-     (%dfe-enum-symbols (args ...) ()))
-    ((_ (symbol => value args ...) (syms ...))
-     (%dfe-enum-symbols (args ...) (syms ... symbol)))
-    ((_ (symbol args ...) (syms ...))
-     (%dfe-enum-symbols (args ...) (syms ... symbol)))
-    ((_ () (syms ...))
-     '(syms ...))))
-
-(define-syntax %dfe-enum-values
-  (syntax-rules (=>)
-    ((_ (args ...))
-     (%dfe-enum-values (args ...) () -1))
-    ((_ (symbol => value args ...) (vals ...) previous)
-     (%dfe-enum-values (args ...) (vals ... value) value))
-    ((_ (symbol args ...) (vals ...) previous)
-     (%dfe-enum-values (args ...) (vals ... (1+ previous)) (1+ previous)))
-    ((_ () (vals ...) previous)
-     (list vals ...))))
-
-;;; Pointer types
-
-(define-syntax-rule (define-foreign-pointer-type pointer-name record-type
-                      record? pointer->record record->pointer)
-  (begin
-    (define-wrapped-pointer-type record-type
-      record? pointer->record record->pointer
-      (lambda (rec port)
-        (let ((address (ffi:pointer-address (record->pointer rec))))
-          (format port "#<~a 0x~x>" 'pointer-name address))))
-    (define-foreign-type pointer-name pointer
-      pointer->record record->pointer)))
-
-;;; Function wrappers
-
-(define-syntax-rule (define-foreign-library library path args ...)
-  (define library
-    (load-foreign-library path args ...)))
-
-(define-syntax-rule (define-foreign-function library
-                      (function-name signature ...))
-  (define function-name
-    (apply wrapped-foreign-library-function library
-           (symbol->string 'function-name)
-           (%dff-parse-signature (signature ...)))))
-
-(define-syntax %dff-parse-signature
-  (syntax-rules (->)
-    ((_ (-> return-type) arg-types ...)
-     (list #:return-type return-type
-           #:arg-types (list arg-types ...)))
-    ((_ (next rest ...) arg-types ...)
-     (%dff-parse-signature (rest ...) arg-types ... next))))
-
-(define-syntax-rule (define-foreign-functions library
-                      (function-name signature ...) ...)
-  (begin
-    (define-foreign-function library
-      (function-name signature ...))
-    ...))
-
-(define* (wrapped-foreign-library-function library function-name
-                                           #:key return-type arg-types)
-  (let ((wrap-result (c-type-wrapper return-type))
-        (arg-unwrappers (map c-type-unwrapper arg-types))
-        (foreign-function
-         (foreign-library-function library function-name
-                                   #:return-type (c-type-repr return-type)
-                                   #:arg-types (map c-type-repr arg-types))))
-    (lambda args
-      (wrap-result
-       (apply foreign-function
-              (map (lambda (unwrap arg) (unwrap arg))
-                   arg-unwrappers args))))))

+ 0 - 214
openai/utils/magick.scm

@@ -1,214 +0,0 @@
-;;; guile-openai --- An OpenAI API client for Guile
-;;; Copyright © 2023 Andrew Whatson <whatson@tailcall.au>
-;;;
-;;; This file is part of guile-openai.
-;;;
-;;; guile-openai is free software: you can redistribute it and/or modify
-;;; it under the terms of the GNU Affero General Public License as
-;;; published by the Free Software Foundation, either version 3 of the
-;;; License, or (at your option) any later version.
-;;;
-;;; guile-openai 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
-;;; Affero General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Affero General Public
-;;; License along with guile-openai.  If not, see
-;;; <https://www.gnu.org/licenses/>.
-
-(define-module (openai utils magick)
-  #:use-module (openai utils foreign)
-  #:use-module (ice-9 match)
-  #:use-module (rnrs bytevectors)
-  #:use-module (srfi srfi-9)
-  #:use-module ((system foreign) #:prefix ffi:)
-  #:export (magick-alpha-channel-options
-            magick-convert-image))
-
-(define-foreign-library libMagickWand
-  (string-append
-   "/gnu/store/v9fjvaqssbw7ikkl0zmmh7jgy49374n6-"
-   "imagemagick-6.9.12-4/lib/libMagickWand-6.Q16.so"))
-
-(define-foreign-enum-type alpha-channel-enum int
-  alpha-channel? alpha-channel-list
-  int->alpha-channel alpha-channel->int
-  (Undefined
-   Activate
-   Associate
-   Background
-   Copy
-   Deactivate
-   Discrete
-   Disassociate
-   Extract
-   Off
-   On
-   Opaque
-   Remove
-   Set
-   Shape
-   Transparent))
-
-(define-foreign-enum-type exception-type-enum int
-  exception-type? exception-type-list
-  int->exception-type exception-type->int
-  (UndefinedException
-   WarningException => 300
-   ResourceLimitWarning => 300
-   TypeWarning => 305
-   OptionWarning => 310
-   DelegateWarning => 315
-   MissingDelegateWarning => 320
-   CorruptImageWarning => 325
-   FileOpenWarning => 330
-   BlobWarning => 335
-   StreamWarning => 340
-   CacheWarning => 345
-   CoderWarning => 350
-   FilterWarning => 352
-   ModuleWarning => 355
-   DrawWarning => 360
-   ImageWarning => 365
-   WandWarning => 370
-   RandomWarning => 375
-   XServerWarning => 380
-   MonitorWarning => 385
-   RegistryWarning => 390
-   ConfigureWarning => 395
-   PolicyWarning => 399
-   ErrorException => 400
-   ResourceLimitError => 400
-   TypeError => 405
-   OptionError => 410
-   DelegateError => 415
-   MissingDelegateError => 420
-   CorruptImageError => 425
-   FileOpenError => 430
-   BlobError => 435
-   StreamError => 440
-   CacheError => 445
-   CoderError => 450
-   FilterError => 452
-   ModuleError => 455
-   DrawError => 460
-   ImageError => 465
-   WandError => 470
-   RandomError => 475
-   XServerError => 480
-   MonitorError => 485
-   RegistryError => 490
-   ConfigureError => 495
-   PolicyError => 499
-   FatalErrorException => 700
-   ResourceLimitFatalError => 700
-   TypeFatalError => 705
-   OptionFatalError => 710
-   DelegateFatalError => 715
-   MissingDelegateFatalError => 720
-   CorruptImageFatalError => 725
-   FileOpenFatalError => 730
-   BlobFatalError => 735
-   StreamFatalError => 740
-   CacheFatalError => 745
-   CoderFatalError => 750
-   FilterFatalError => 752
-   ModuleFatalError => 755
-   DrawFatalError => 760
-   ImageFatalError => 765
-   WandFatalError => 770
-   RandomFatalError => 775
-   XServerFatalError => 780
-   MonitorFatalError => 785
-   RegistryFatalError => 790
-   ConfigureFatalError => 795
-   PolicyFatalError => 799))
-
-(define-foreign-pointer-type wand-ptr <wand>
-  wand? pointer->wand wand->pointer)
-
-(define-foreign-functions libMagickWand
-  (MagickWandGenesis -> void)
-  (MagickWandTerminus -> void)
-  (NewMagickWand -> wand-ptr)
-  (DestroyMagickWand wand-ptr -> wand-ptr)
-  (MagickGetException wand-ptr pointer -> cstring)
-  (MagickReadImage wand-ptr cstring -> bool)
-  (MagickReadImageBlob wand-ptr pointer size_t -> bool)
-  (MagickWriteImage wand-ptr cstring -> bool)
-  (MagickGetImageBlob wand-ptr pointer -> pointer)
-  (MagickGetImageFormat wand-ptr -> cstring)
-  (MagickSetImageFormat wand-ptr cstring -> bool)
-  (MagickGetImageAlphaChannel wand-ptr -> alpha-channel-enum)
-  (MagickSetImageAlphaChannel wand-ptr alpha-channel-enum -> bool))
-
-(set! MagickGetException
-      (let ((func MagickGetException))
-        (lambda (wand)
-          (let* ((type-ptr (ffi:make-c-struct (list ffi:int) (list 0)))
-                 (message (func wand type-ptr))
-                 (type-int (car (ffi:parse-c-struct type-ptr (list ffi:int))))
-                 (type (int->exception-type type-int)))
-            (list type message)))))
-
-(set! MagickReadImage
-      (let ((func MagickReadImage))
-        (lambda (wand path)
-          (unless (func wand path)
-            (error "MagickWand error:" (MagickGetException wand))))))
-
-(set! MagickReadImageBlob
-      (let ((func MagickReadImageBlob))
-        (lambda (wand bv)
-          (let ((ptr (ffi:bytevector->pointer bv))
-                (len (bytevector-length bv)))
-            (unless (func wand ptr len)
-              (error "MagickWand error:" (MagickGetException wand)))))))
-
-(set! MagickGetImageBlob
-      (let ((func MagickGetImageBlob))
-        (lambda (wand)
-          (let* ((len-ptr (ffi:make-c-struct (list ffi:size_t) (list 0)))
-                 (data-ptr (func wand len-ptr))
-                 (data-len (car (ffi:parse-c-struct len-ptr (list ffi:size_t)))))
-            (ffi:pointer->bytevector data-ptr data-len)))))
-
-(set! MagickSetImageFormat
-      (let ((func MagickSetImageFormat))
-        (lambda (wand format)
-          (unless (func wand format)
-            (error "MagickWand error:" (MagickGetException wand))))))
-
-(set! MagickSetImageAlphaChannel
-      (let ((func MagickSetImageAlphaChannel))
-        (lambda (wand alpha-channel)
-          (unless (func wand alpha-channel)
-            (error "MagickWand error:" (MagickGetException wand))))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (call-with-magick-wand proc)
-  (let ((wand #f))
-    (dynamic-wind
-      (const #t)
-      (lambda ()
-        (MagickWandGenesis)
-        (set! wand (NewMagickWand))
-        (proc wand))
-      (lambda ()
-        (when wand
-          (DestroyMagickWand wand))))))
-
-(define (magick-alpha-channel-options)
-  (alpha-channel-list))
-
-(define* (magick-convert-image image-bytes #:key
-                               format
-                               alpha-channel)
-  (call-with-magick-wand
-   (lambda (wand)
-     (MagickReadImageBlob wand image-bytes)
-     (MagickSetImageFormat wand format)
-     (MagickSetImageAlphaChannel wand alpha-channel)
-     (MagickGetImageBlob wand))))

+ 13 - 0
pre-inst-env.in

@@ -0,0 +1,13 @@
+#!/bin/sh
+
+abs_top_srcdir="`cd "@abs_top_srcdir@" > /dev/null; pwd`"
+abs_top_builddir="`cd "@abs_top_builddir@" > /dev/null; pwd`"
+
+GUILE_LOAD_COMPILED_PATH="$abs_top_builddir${GUILE_LOAD_COMPILED_PATH:+:}$GUILE_LOAD_COMPILED_PATH"
+GUILE_LOAD_PATH="$abs_top_builddir:$abs_top_srcdir${GUILE_LOAD_PATH:+:}:$GUILE_LOAD_PATH"
+export GUILE_LOAD_COMPILED_PATH GUILE_LOAD_PATH
+
+GUILE_AUTO_COMPILE=0
+export GUILE_AUTO_COMPILE
+
+exec "$@"