Idiomatic Clojure wrapper over the stable OpenAI API surface exposed by the official Java SDK.
deps.edn:
net.clojars.savya/openai-clj {:mvn/version "0.7.0"}
Leiningen:
[net.clojars.savya/openai-clj "0.7.0"]
Tracks com.openai/openai-java 4.42.0.
(require '[openai.core :as openai])
(def client (openai/client)) ; reads OPENAI_API_KEY
(def configured-client
(openai/client {:api-key "sk-..."
:organization "org_..."
:project "proj_..."
:base-url "https://api.openai.com/v1"
:timeout-ms 60000
:max-retries 2}))
(openai/create-response
client
{:model "gpt-5.2"
:input "Write one sentence about Clojure maps."
:instructions "Be precise."
:max-output-tokens 256
:temperature 0.2
:top-p 1.0
:metadata {:app "docs"}
:store true
:reasoning {:effort :low}})
;; => {:id "resp_..."
;; :model "gpt-5.2"
;; :status :completed
;; :output [{:type :message
;; :role :assistant
;; :id "msg_..."
;; :content [{:type :text :text "Clojure maps are ..."}]}]
;; :text "Clojure maps are ..."
;; :usage {:input-tokens 14 :output-tokens 12 :total-tokens 26}
;; :created-at 1790000000.0}
Request maps support :model, :input, :instructions,
:max-output-tokens, :max-tool-calls, :temperature, :top-p,
:top-logprobs, :metadata, :previous-response-id, :store, :user,
:reasoning, :tools, :tool-choice, :parallel-tool-calls, :background,
:include, :truncation, :prompt-cache-key, :safety-identifier,
:service-tier, :json-schema, :verbosity (:low/:medium/:high),
:conversation (a conversation id string), :stream-options
({:include-obfuscation true}), and :moderation ({:model "..."}).
Input can be a string or a vector of message items. Message content can be a string or a vector of multimodal parts:
{:model "gpt-5.2"
:input [{:role :user
:content [{:type :text :text "Summarize this image."}
{:type :image
:image-url "https://example.test/chart.png"
:detail :high}
{:type :file
:filename "notes.pdf"
:file-data "data:application/pdf;base64,..."}]}]}
Structured outputs use :json-schema:
{:model "gpt-5.2"
:input "Return an answer object."
:json-schema {:name "answer"
:description "One answer"
:strict true
:schema {:type "object"
:properties {:answer {:type "string"}}
:required ["answer"]}}}
Prefer the Responses API for new OpenAI work. Chat Completions is exposed as the compatibility path for OpenAI-compatible endpoints that do not support Responses, including local LLMs and hosted compat providers.
(openai/create-chat-completion
client
{:model "gpt-4o-mini"
:messages [{:role :system :content "Be terse."}
{:role :user :content "Write one sentence about Clojure maps."}]})
;; => {:id "chatcmpl_..."
;; :model "gpt-4o-mini"
;; :created 1790000000
;; :choices [{:index 0
;; :finish-reason :stop
;; :message {:role :assistant
;; :content "Clojure maps are ..."}}]
;; :text "Clojure maps are ..."
;; :usage {:prompt-tokens 14 :completion-tokens 12 :total-tokens 26}}
Function tools use the same JSON-schema-shaped :parameters maps as Responses:
(openai/create-chat-completion
client
{:model "gpt-4o-mini"
:messages [{:role :user :content "Weather in Denver?"}]
:tools [{:type :function
:name "get_weather"
:description "Get current weather"
:strict true
:parameters {:type "object"
:properties {:location {:type "string"}}
:required ["location"]}}]
:tool-choice {:type :function :name "get_weather"}})
Streaming returns the concatenated content and calls the callback for each normalized chunk:
(openai/stream-chat-completion-text
client
{:model "gpt-4o-mini"
:messages [{:role :user :content "Count to three."}]
:stream-options {:include-usage true}}
println)
All functions take an openai.core/client as their first argument and accept
kebab-case request maps.
(require '[openai.images :as images]
'[openai.audio :as audio]
'[openai.moderations :as moderations]
'[openai.completions :as completions]
'[openai.vector-stores :as vector-stores]
'[openai.uploads :as uploads]
'[openai.containers :as containers]
'[openai.conversations :as conversations]
'[openai.fine-tuning :as fine-tuning]
'[openai.evals :as evals]
'[openai.skills :as skills]
'[openai.videos :as videos]
'[openai.webhooks :as webhooks]
'[openai.admin :as admin]
'[openai.admin.projects :as admin-projects])
(images/generate client {:model "gpt-image-1" :prompt "A Clojure logo"})
(audio/create-speech client {:model "gpt-4o-mini-tts" :voice :alloy
:input "Hello"})
(moderations/create client {:input "text"})
(completions/create client {:model "gpt-3.5-turbo-instruct" :prompt "Once"})
(vector-stores/create client {:name "docs" :file-ids ["file_..."]})
(uploads/create client {:filename "data.jsonl" :bytes 100
:mime-type "application/jsonl" :purpose :fine-tune})
(containers/create client {:name "sandbox"})
(conversations/create client {:items [{:role :user :content "Hello"}]})
(fine-tuning/create-job client {:model "gpt-4.1-mini"
:training-file "file_..."})
(evals/list client {:limit 20})
(skills/list client {:limit 20})
(videos/create client {:model "sora-2" :prompt "Ocean sunrise"
:size "1280x720" :seconds "8"})
(webhooks/unwrap webhook-client raw-body request-headers)
(admin/project-list admin-client {:limit 20})
(admin-projects/service-account-list admin-client {:project-id "proj_..."})
openai.core also contains Responses, Chat Completions, embeddings, files,
batches, models, and stored Chat Completions. openai.graders reflects the
stable grader-model service, which exposes no operations in SDK 4.42.0.
Out of scope: beta APIs, realtime WebSockets, async clients, raw-response
accessors, and per-call RequestOptions.
clojure -M:test
Unit tests are no-network; ^:integration tests, if added later, should be
skipped without OPENAI_API_KEY.
Copyright © 2026 Savyasachi.
Distributed under the Eclipse Public License 2.0.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |