2  Using Large Language Models from Clojure

(ns llms 
  (:require
   [org.httpkit.client :as hk-client]
   [cheshire.core :as json]))

LLMs often come as APIs, as they require computing power (GPUs), which most users do not have locally. OpenAI offers their models behind an (paid) API for example. In the following we will see three different ways to use the GPT-4 model from OpenAI

Get the openai API key either from environment or a specific file

(def open-ai-key
  (or (System/getenv "OPEN_AI_KEY")
      (slurp "open_ai_secret.txt")
      )
  )

2.1 Use OpenAI API directly

OpenAI offers a rather simple API, text-in text-out for “chatting” with GPT

The following shows how to ask a simple question, and getting the answer using an http library, http-kit. The API is based on JSON, so easy to use from Clojure

(->
 @(hk-client/post "https://api.openai.com/v1/chat/completions"
                  {:headers
                   {"content-type" "application/json"
                    "authorization" (format "Bearer %s" open-ai-key)}
                   :body
                   (json/encode
                    {:model "gpt-4"
                     :messages [{:role "system",
                                 :content "You are a helpful assistant."},
                                {:role "user",
                                 :content "What is Clojure ?"}]})})
 :body
 (json/decode keyword))
{:id "chatcmpl-AeLIWh2l733ziLLu6CJoxPXDTPTD4",
 :object "chat.completion",
 :created 1734178352,
 :model "gpt-4-0613",
 :choices
 [{:index 0,
   :message
   {:role "assistant",
    :content
    "Clojure is a modern, dynamic, and functional dialect of the Lisp programming language on the Java platform. It is designed to be a general-purpose language, combining the approachability and interactive development of a scripting language with an efficient and robust infrastructure for multithreaded programming. \n\nClojure features a rich set of immutable, persistent data structures, first-class functions and dynamic typing. It supports a software transactional memory system and promotes a functional programming style. It is widely used for a variety of tasks, from web development to data analysis and artificial intelligence.",
    :refusal nil},
   :logprobs nil,
   :finish_reason "stop"}],
 :usage
 {:prompt_tokens 22,
  :completion_tokens 111,
  :total_tokens 133,
  :prompt_tokens_details {:cached_tokens 0, :audio_tokens 0},
  :completion_tokens_details
  {:reasoning_tokens 0,
   :audio_tokens 0,
   :accepted_prediction_tokens 0,
   :rejected_prediction_tokens 0}},
 :system_fingerprint nil}

2.2 use Bosquet

Bosquet abstracts some of the concepts of LLMs on a higher level API. Its has further notions of “memory” and “tools” and has other features we find for example in python “LangChain”

Bosque wants the API key in a config file

(spit "secrets.edn"
 (pr-str
  {:openai  {:api-key open-ai-key}}))
nil
(require '[bosquet.llm.generator :refer [generate llm]])

Call GPT from Bosquet

(generate
 [[:user "What is Clojure"]
  [:assistant (llm :openai
                   :llm/model-params {:model :gpt-4
                                      })]])
#:bosquet{:conversation
          [[:user "What is Clojure"]
           [:assistant
            "Clojure is a high-level, dynamic, functional programming language. It is designed to be a general-purpose language, combining the approachability and interactive development of a scripting language with an efficient and robust infrastructure for multithreaded programming. Clojure is a dialect of Lisp, and shares with Lisp the code-as-data philosophy and a powerful macro system. It runs on the Java Virtual Machine (JVM), the Common Language Runtime (CLR), and JavaScript platforms."]],
          :completions
          {nil
           "Clojure is a high-level, dynamic, functional programming language. It is designed to be a general-purpose language, combining the approachability and interactive development of a scripting language with an efficient and robust infrastructure for multithreaded programming. Clojure is a dialect of Lisp, and shares with Lisp the code-as-data philosophy and a powerful macro system. It runs on the Java Virtual Machine (JVM), the Common Language Runtime (CLR), and JavaScript platforms."},
          :usage
          {nil {:prompt 11, :completion 92, :total 103},
           :bosquet/total {:prompt 11, :completion 92, :total 103}},
          :time 3790}

2.3 Use langchain4j

We can use LLMs as well via a Java Interop and the library lnagchain4j which aims to be a copy of the python library langchain, and offers support or building blocks for several concepts around LLMs (model, vector stores, document loaders, etc.) We see it used in the following chapters

(import '[dev.langchain4j.model.openai OpenAiChatModel OpenAiChatModelName])
dev.langchain4j.model.openai.OpenAiChatModelName

For now just the simplest call to an GPT model, asking it the same question:

(def open-ai-chat-model
  (.. (OpenAiChatModel/builder)
      (apiKey open-ai-key)
      (modelName OpenAiChatModelName/GPT_4)
      build))
(.generate open-ai-chat-model "What is Clojure ?")
"Clojure is a modern, dynamic, and functional dialect of the Lisp programming language on the Java platform. It is designed to be a general-purpose language, combining the approachability and interactive development of a scripting language with an efficient and robust infrastructure for multithreaded programming."
source: projects/ml/llm/notebooks/llms.clj