chat.scm 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. ;;; guile-openai --- An OpenAI API client for Guile
  2. ;;; Copyright © 2023 Andrew Whatson <whatson@tailcall.au>
  3. ;;;
  4. ;;; This file is part of guile-openai.
  5. ;;;
  6. ;;; guile-openai is free software: you can redistribute it and/or modify
  7. ;;; it under the terms of the GNU Affero General Public License as
  8. ;;; published by the Free Software Foundation, either version 3 of the
  9. ;;; License, or (at your option) any later version.
  10. ;;;
  11. ;;; guile-openai is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. ;;; Affero General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU Affero General Public
  17. ;;; License along with guile-openai. If not, see
  18. ;;; <https://www.gnu.org/licenses/>.
  19. (define-module (openai chat)
  20. #:use-module (openai api chat)
  21. #:use-module (openai client)
  22. #:use-module (openai utils colorized)
  23. #:use-module (ice-9 match)
  24. #:use-module (srfi srfi-9)
  25. #:use-module (srfi srfi-9 gnu)
  26. #:use-module (srfi srfi-41)
  27. #:export (openai-default-chat-model
  28. openai-default-chat-temperature
  29. openai-default-chat-top-p
  30. chat?
  31. chat-content
  32. chat-stream
  33. openai-chat))
  34. (define-once openai-default-chat-model
  35. (make-parameter 'gpt-3.5-turbo))
  36. (define-once openai-default-chat-temperature
  37. (make-parameter *unspecified*))
  38. (define-once openai-default-chat-top-p
  39. (make-parameter *unspecified*))
  40. (define-once openai-default-chat-stream?
  41. (make-parameter #t))
  42. (define-record-type <Chat>
  43. (%make-chat content stream)
  44. chat?
  45. (content %chat-content)
  46. (stream chat-stream))
  47. (define (chat-content chat)
  48. (force (%chat-content chat)))
  49. (define (make-chats result n)
  50. (define make-one-chat
  51. (if (stream? result)
  52. (lambda (ix)
  53. (let* ((strm (responses->content-stream result ix))
  54. (cont (delay (string-concatenate (stream->list strm)))))
  55. (%make-chat cont strm)))
  56. (lambda (ix)
  57. (let* ((cont (delay (response->content result ix)))
  58. (strm (stream (force cont))))
  59. (%make-chat cont strm)))))
  60. (apply values (map make-one-chat (iota n))))
  61. (define (response->content response n)
  62. (chat-message-content
  63. (chat-choice-message
  64. (list-ref (chat-response-choices response) n))))
  65. (define (responses->content-stream responses n)
  66. (stream-let loop ((responses responses))
  67. (if (stream-null? responses)
  68. stream-null
  69. (let* ((response (stream-car responses))
  70. (choice (car (chat-response-choices response)))
  71. (index (chat-choice-index choice))
  72. (delta (chat-choice-delta choice))
  73. (content (chat-message-content delta)))
  74. (cond ((not (eqv? index n)) ;; ignore wrong index
  75. (loop (stream-cdr responses)))
  76. ((unspecified? content) ;; ignore unspecified content
  77. (loop (stream-cdr responses)))
  78. (else
  79. (stream-cons content (loop (stream-cdr responses)))))))))
  80. (define (print-chat chat port)
  81. (newline port)
  82. (stream-for-each (lambda (content)
  83. (display content port))
  84. (chat-stream chat)))
  85. (set-record-type-printer! <Chat> print-chat)
  86. (add-color-scheme! `((,chat? CHAT ,color-stream (GREEN BOLD))))
  87. (define parse-prompt
  88. (match-lambda
  89. ((? pair? msgs)
  90. (map parse-message msgs))
  91. (msg
  92. (list (parse-message msg)))))
  93. (define parse-message
  94. (match-lambda
  95. ((? string? msg)
  96. (make-chat-message "user" msg))
  97. (((and role (or 'system 'user 'assistant)) . (? string? msg))
  98. (make-chat-message (symbol->string role) msg))))
  99. (define* (openai-chat prompt #:key
  100. (model (openai-default-chat-model))
  101. (temperature (openai-default-chat-temperature))
  102. (top-p (openai-default-chat-top-p))
  103. (n *unspecified*)
  104. (stream? (openai-default-chat-stream?))
  105. (stop *unspecified*)
  106. (max-tokens *unspecified*)
  107. (presence-penalty *unspecified*)
  108. (frequency-penalty *unspecified*)
  109. (logit-bias *unspecified*)
  110. (user (openai-default-user)))
  111. "Send a chat completion request. Returns a chat record.
  112. The PROMPT can be a string, which will be sent as a user message.
  113. Alternatively, prompt can be a list of `(role . content)' pairs, where
  114. content is a string and role is a symbol `system', `user', or
  115. `assistant'.
  116. The keyword arguments correspond to the request parameters described
  117. in the chat completion request documentation:
  118. #:n - The number of responses to generate, returned as multiple
  119. values.
  120. #:stream? - Whether to stream the response(s), defaults to `#t'.
  121. #:model - A symbol or string identifying the model to use. Defaults
  122. to `gpt-3.5-turbo', but if you're lucky you might be able to use
  123. `gpt-4' or `gpt-4-32k' here.
  124. #:temperature - The sampling temperature to use, a number between 0
  125. and 2.
  126. #:top-p - An alternative sampling parameter, a number between 0 and 1.
  127. #:user - An optional username to associate with this request.
  128. The `#:stop', `#:max-tokens', `#:logit-bias', `#:presence-penalty',
  129. `#:frequency-penalty' parameters are implemented but untested."
  130. (let* ((model (if (symbol? model) (symbol->string model) model))
  131. (prompt (parse-prompt prompt))
  132. (stream? (or stream? *unspecified*))
  133. (request (make-chat-request model prompt
  134. temperature top-p n stream? stop max-tokens
  135. presence-penalty frequency-penalty logit-bias user))
  136. (response (send-chat-request request)))
  137. (make-chats response (if (unspecified? n) 1 n))))