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-AjW6ECkx9x4cjSmxs2St2xHCI4JHS",
 :object "chat.completion",
 :created 1735411514,
 :model "gpt-4-0613",
 :choices
 [{:index 0,
   :message
   {:role "assistant",
    :content
    "Clojure is a compiled, dynamic, general-purpose programming language that combines the approachability and interactive development of a scripting language with an efficient and robust infrastructure for multithreaded programming. It is a dialect of the Lisp programming language that runs on the Java Virtual Machine (JVM), JavaScript engines, and the CLR (.NET platform).\n\nClojure promotes immutability and immutable data structures. Being a functional programming language, it emphasizes recursive iteration instead of looping, and treats functions as first-class objects. \n\nIt also provides easy access to Java frameworks, with optional type hints and type inference, to ensure that calls to Java can avoid reflection and enable fast performance.",
    :refusal nil},
   :logprobs nil,
   :finish_reason "stop"}],
 :usage
 {:prompt_tokens 22,
  :completion_tokens 133,
  :total_tokens 155,
  :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 dynamic, general-purpose programming language that runs on the Java Virtual Machine (JVM). It is a dialect of the Lisp programming language and focuses on providing a simple and holistic approach to programming. Clojure’s key features include immutability, functional programming, concurrency, and polymorphism. It was designed by Rich Hickey and was first released in 2007."]],
          :completions
          {nil
           "Clojure is a dynamic, general-purpose programming language that runs on the Java Virtual Machine (JVM). It is a dialect of the Lisp programming language and focuses on providing a simple and holistic approach to programming. Clojure’s key features include immutability, functional programming, concurrency, and polymorphism. It was designed by Rich Hickey and was first released in 2007."},
          :usage
          {nil {:prompt 11, :completion 78, :total 89},
           :bosquet/total {:prompt 11, :completion 78, :total 89}},
          :time 3830}

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. Clojure is predominantly a functional programming language and features a rich set of immutable, persistent data structures."
source: projects/ml/llm/notebooks/llms.clj