A production-quality Clojure SDK for LLM providers: one canonical API for chat, embeddings, moderation, rerank, image generation, audio transcription, and text-to-speech.
This is a provider SDK, not an agent framework or proxy server. It owns provider wire-format differences so your application does not have to. It does not include credential pools, budget routing, plugin loading, vector stores, MCP clients, observability sinks, or secret managers.
Add the library to deps.edn (released to Clojars):
{:deps {net.clojars.deadmeme5441/clojure-llm-sdk {:mvn/version "0.5.0"}}}
Or with Leiningen / project.clj:
[net.clojars.deadmeme5441/clojure-llm-sdk "0.5.0"]
Upgrading from 0.4.x: 0.5.0 includes breaking provider, streaming, and usage changes. Review the migration notes before upgrading.
Then require the public namespace:
(require '[llm.sdk :as sdk])
(sdk/complete
:openai
{:request/model "gpt-4o-mini"
:request/messages [{:message/role :user
:message/content "Reply with the single word: ok"}]})
Responses use the same canonical shape across providers:
{:response/provider :openai
:response/model "gpt-4o-mini"
:response/parts [{:part/type :text
:text "ok"}]
:response/finish-reason :stop
:response/usage {...}
:response/cost {...}
:response/cache {...}
:response/provider-data {...}
:response/raw {...}}
Streaming uses the same request shape:
(sdk/complete
:openai
{:request/model "gpt-4o-mini"
:request/messages [{:message/role :user
:message/content "Count to three"}]}
:stream? true
:on-event (fn [event]
(when (= :stream/content-delta (:event/type event))
(print (:event/delta event)))))
Library docs:
unravel-team/litellm-clj library.Project docs:
The public API exposes these canonical surfaces:
| Modality | Public function | Providers |
|---|---|---|
| Chat | sdk/complete | OpenAI, Anthropic, Anthropic on Vertex, Gemini, Vertex Gemini, Z.AI, OpenRouter, Codex, DeepSeek, Kimi, Kimi Code, Mistral, Groq, Cerebras, Together, xAI, HuggingFace Router, Perplexity, Bedrock, Ollama, and aggregator aliases |
| Embeddings | sdk/embed | OpenAI, Gemini Native, OpenRouter, Azure OpenAI deployment profiles, Cohere, Voyage, Mistral, Together, Jina, Nebius, and Ollama |
| Moderation | sdk/moderate | OpenAI |
| Rerank | sdk/rerank | Cohere, Voyage, Jina |
| Image generation | sdk/generate-image | OpenAI, OpenRouter, Vertex Gemini image generation (:vertex-imagen), and Bedrock image models |
| Audio transcription | sdk/transcribe | OpenAI Whisper, Groq Whisper |
| Text-to-speech | sdk/speak | OpenAI TTS, ElevenLabs |
See Providers for the full provider matrix and credential list. These rows describe implemented constructors, not a promise that every catalog model is currently served or enabled for an account or region.
Offline model and pricing lookup merges bundled snapshots generated from
LiteLLM revision b1a61f5
and models.dev revision a2a6739.
Entries retain source provenance;
missing usage or rates remain unknown rather than becoming zero or borrowing a
rate from another modality. Live listing is available only for providers with a
supported model-list endpoint and reports that endpoint's account-visible view.
OpenAI, OpenRouter, and Bedrock image calls require an explicit
:image/model; none of these providers selects a billable image model
implicitly.
Credentials are read from environment variables. The SDK does not load .env files itself.
export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-ant-api03-...
export GEMINI_API_KEY=AIza...
export ZAI_API_KEY=...
export KIMI_API_KEY=...
Use .env.example as a non-secret template for local live smoke tests.
Some provider names are intentionally distinct:
:kimi uses Moonshot's public API and reads MOONSHOT_API_KEY.:kimi-code uses Kimi Code's coding endpoint and reads KIMI_API_KEY.:vertex-gemini uses Google Application Default Credentials or GOOGLE_OAUTH_ACCESS_TOKEN.:zai uses Z.AI's OpenAI-compatible GLM endpoint and reads ZAI_API_KEY.:vertex-anthropic serves Claude models through Google Vertex AI using the same GCP credentials as :vertex-gemini, not an ANTHROPIC_API_KEY.:codex-backend reads OAuth data from the official Codex CLI auth file. HTTP/SSE remains the latency-oriented default; :config {:transport :websocket} enables persistent Responses WebSockets with incremental history reuse. :openai and API-key :codex are unchanged.Applications that manage secrets outside environment variables can pass per-call runtime config:
(sdk/complete :openai request
:config {:api-key "sk-..."
:base-url "https://api.openai.com/v1"
:timeout-ms 60000})
:cost/usd :unknown; missing cache telemetry returns :cache/status :unknown.Embeddings:
(sdk/embed
:openai
{:embed/model "text-embedding-3-small"
:embed/inputs ["clojure" "lisp" "java"]})
Native Gemini embeddings use the existing :gemini-native profile:
(sdk/embed
:gemini-native
{:embed/model "gemini-embedding-001"
:embed/inputs ["clojure" "lisp" "java"]
:embed/dimensions 256
:embed/provider-options {:task-type :retrieval-document}})
Rerank:
(sdk/rerank
:cohere
{:rerank/model "rerank-english-v3.0"
:rerank/query "lisp dialect on the JVM"
:rerank/documents ["Python" "Clojure" "JavaScript"]
:rerank/top-n 3})
Fallbacks:
(sdk/with-fallbacks
[[:openai "gpt-4o"]
[:anthropic "claude-haiku-4-5"]
[:groq "llama-3.1-8b-instant"]]
{:request/messages [{:message/role :user
:message/content "Reply: ok"}]})
Custom OpenAI-compatible alias:
(require '[llm.sdk.providers.openai.chat :as openai-chat])
(openai-chat/register-alias!
{:id :my-private-llm
:base-url "https://llm.example.com/v1"
:env-var-names ["MY_PRIVATE_LLM_KEY"]
:capabilities #{:chat :streaming :tools}})
Provider implementations are split by provider family, such as llm.sdk.providers.openai.chat, llm.sdk.providers.anthropic.chat, llm.sdk.providers.gemini.embeddings, llm.sdk.providers.zai.chat, and llm.sdk.providers.openrouter.chat. Older flat namespaces remain compatibility shims for existing code.
The public CI workflow runs:
clj-kondo --lint src test
clojure -M:test
clojure -T:build jar
Live tests are explicit:
cp .env.example .env
set -a; source .env; set +a
clojure -M:live-test
MIT
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 |