Clojure client for the OpenAI API and OpenAI-compatible providers. It uses the official Java SDK.
deps.edn:
net.clojars.savya/openai-clj {:mvn/version "0.19.0"}
Leiningen:
[net.clojars.savya/openai-clj "0.19.0"]
Supported Clojure versions: 1.10, 1.11, and 1.12.
Tracks com.openai/openai-java 4.51.0.
Amazon Bedrock support is optional. Add the :bedrock alias to use the
blocking Bedrock transport:
(require '[openai.bedrock :as bedrock])
(def client
(bedrock/client {:endpoint :runtime
:aws-region "us-west-2"
:aws-profile "default"}))
The alias adds com.openai/openai-java-bedrock and its AWS SDK dependencies;
default consumers do not load them. Credentials can also be supplied with
:aws-access-key-id, :aws-secret-access-key, and :aws-session-token, or
discovered through the AWS credential chain.
Every function takes a client. The client :base-url points to an endpoint
that uses the OpenAI wire protocol. The same code works with these providers:
| Provider | :base-url |
|---|---|
| OpenAI (default) | omit |
| Azure OpenAI | your resource endpoint (see Azure OpenAI) |
| Groq | https://api.groq.com/openai/v1 |
| DeepSeek | https://api.deepseek.com |
| Mistral | https://api.mistral.ai/v1 |
| xAI (Grok) | https://api.x.ai/v1 |
| Together | https://api.together.xyz/v1 |
| Fireworks | https://api.fireworks.ai/inference/v1 |
| Local (Ollama, vLLM, LM Studio) | e.g. http://localhost:11434/v1 |
(def client (openai/client {:api-key "gsk_..."
:base-url "https://api.groq.com/openai/v1"}))
(openai/create-chat-completion
client
{:model "llama-3.3-70b-versatile"
:messages [{:role :user :content "Hello"}]})
Use Chat Completions (create-chat-completion) with the most providers. The
Responses API is OpenAI-specific. Most compatible providers do not implement it.
Only routes that a provider serves work. The library does not cover
provider-specific extensions outside the OpenAI protocol.
(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}))
The constructor also accepts `:admin-api-key`, `:headers`, `:proxy` (a
`java.net.Proxy` or `{:host "..." :port 8080}`), `:executor`,
`:stream-handler-executor`, `:log-level` (`:off`, `:info`, `:error`, or
`:debug`), and a typed SDK `:workload-identity`.
(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:
(def request
{: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"]}}})
Parse and validate the returned JSON against the same schema:
(def response (openai/create-response client request))
(openai/parse-structured-output response (:json-schema request))
;; => {:data {"answer" "..."} :errors []}
Responses tools cover :function, :web-search, :file-search,
:code-interpreter, :programmatic-tool-calling, :image-generation,
:computer, :local-shell, :shell, :apply-patch, :custom,
:tool-search, and :mcp. Vector input accepts the matching
:function-call-output, :computer-call-output, :local-shell-call-output,
:shell-call-output, :apply-patch-call-output, :custom-tool-call-output,
:tool-search-output, and :mcp-approval-response items.
Response maps preserve all SDK output-item variants as kebab-case Clojure maps.
Pass :lossless? true to create-response, or as the optional argument to
get-response, cancel-response, and compact, to retain the curated map and
also include the SDK's complete parsed JSON under :openai/raw.
openai/stream normalizes each Responses stream event. It calls its callback
with the resulting :type-keyed map. openai/stream-text wraps text deltas.
openai.realtime provides normalized WebSocket events through callbacks or a
blocking queue:
(require '[openai.realtime :as realtime])
(def connection
(realtime/connect {:api-key (System/getenv "OPENAI_API_KEY")
:model "gpt-realtime"}))
(realtime/send! connection
{:type :session.update
:session {:type :realtime
:instructions "Be concise."}})
(realtime/poll! connection 5000) ; normalized server event, or nil
(realtime/close! connection)
Reliability features are opt-in. :auto-reconnect? true enables exponential
backoff retries; configure :reconnect-max-attempts or
:reconnect-max-duration-ms, plus the base/max delay and jitter options.
:heartbeat-interval-ms enables OkHttp protocol pings for an owned client, and
:idle-timeout-ms emits :connection.idle-timeout when no server messages are
received in that window. Existing behavior and defaults are unchanged.
The namespace also exposes client-secret creation, legacy session and
transcription-session creation, translation client secrets and WebSockets, and
SIP accept-call, hangup-call, refer-call, and reject-call operations.
Use the Responses API for new OpenAI work. Chat Completions supports OpenAI-compatible endpoints that do not support Responses. This includes local LLMs and hosted compatible 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. It 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)
Service functions take an openai.core/client as the first argument. They
accept kebab-case request maps. Realtime WebSockets take a transport config map.
(require '[openai.images :as images]
'[openai.audio :as audio]
'[openai.content-provenance-checks :as cpc]
'[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.chatkit :as chatkit]
'[openai.beta.responses :as beta-responses]
'[openai.realtime :as realtime]
'[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"})
(cpc/create client {:file "image.png"})
(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"})
(chatkit/create-session client {:workflow {:id "wf_123"} :user "user_42"})
(beta-responses/create-response client {:model "gpt-5" :input "Hello"})
(webhooks/unwrap webhook-client raw-body request-headers)
(admin/project-list admin-client {:limit 20})
(admin-projects/service-account-list admin-client "proj_...")
openai.core also contains Responses, Chat Completions, embeddings, files,
batches, models, and stored Chat Completions. openai.realtime contains
WebSocket, session, client-secret, transcription, translation, and SIP call
helpers. openai.content-provenance-checks contains Content Provenance Checks.
openai.graders maps to the stable grader-model service. The service exposes
no operations in SDK 4.51.0.
List functions remain eager by default. Additive lazy siblings cover models,
files, batches, stored Chat Completions, response input items, vector stores and
their files/batches, and ChatKit threads/items. Their option maps accept
:max-items and :max-pages to bound realization.
The library wraps each non-deprecated operation that the Java SDK exposes. This
includes beta ChatKit and beta Responses. The Assistants API (assistants/threads/runs) is not
wrapped because the SDK marks it as deprecated in favor of the Responses API.
Async clients, raw-response accessors, and per-call RequestOptions are
transport and accessor variants, not endpoints. The library does not duplicate them.
clojure -M:test
Unit tests do not use a network. Skip ^:integration tests, if added, 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 |