The real backend: the Anthropic Messages API over raw HTTP.
There is no official Anthropic SDK for Clojure, so raw HTTP is the supported path —
and it costs nothing here, because the repo already carries both halves: cheshire
for JSON and JDK java.net.http, which vaelii.impl.client already speaks to the
vaelii daemon. This namespace mirrors that one: an explicit connection handle, no
global state, no new dependency.
Reached only when a caller installs it (vaelii.impl.llm.stub is the default), so a
build with no credential and no network never loads a socket.
Request shape notes, because several of them are load-bearing:
temperature / top_p / top_k are rejected on this model family — they are
never sent, and steering happens in the prompt instead.output_config.effort. :thinking-display "summarized" opts into a readable
summary (the default omits the text).stop_reason: "refusal" and empty or partial
content, so parse-response never fabricates a text block and the session loop
branches on :stop-reason before touching :content.cache_control breakpoint; the user's turn sits after it. The minimum cacheable
prefix on claude-opus-5 is 512 tokens — a short prompt simply will not cache,
with no error.fallbacks is sent by default so a policy decline is re-served rather than
returned as a dead turn. It rides a beta header; pass {:fallbacks nil} to drop
both if the org has not enabled it.Credentials are resolved from the environment, never hardcoded and never logged
— see credentials.
The real backend: the Anthropic Messages API over raw HTTP.
There is no official Anthropic SDK for Clojure, so raw HTTP is the supported path —
and it costs nothing here, because the repo already carries both halves: `cheshire`
for JSON and JDK `java.net.http`, which `vaelii.impl.client` already speaks to the
vaelii daemon. This namespace mirrors that one: an explicit connection handle, no
global state, **no new dependency**.
Reached only when a caller installs it (`vaelii.impl.llm.stub` is the default), so a
build with no credential and no network never loads a socket.
Request shape notes, because several of them are load-bearing:
* **`temperature` / `top_p` / `top_k` are rejected** on this model family — they are
never sent, and steering happens in the prompt instead.
* **Thinking is on by default** and takes no token budget; depth is set with
`output_config.effort`. `:thinking-display "summarized"` opts into a readable
summary (the default omits the text).
* A **refusal is HTTP 200** with `stop_reason: "refusal"` and empty or partial
content, so `parse-response` never fabricates a text block and the session loop
branches on `:stop-reason` before touching `:content`.
* The generated system prompt is a large stable prefix, so its last block carries a
`cache_control` breakpoint; the user's turn sits after it. The minimum cacheable
prefix on `claude-opus-5` is 512 tokens — a short prompt simply will not cache,
with no error.
* `fallbacks` is sent by default so a policy decline is re-served rather than
returned as a dead turn. It rides a beta header; pass `{:fallbacks nil}` to drop
both if the org has not enabled it.
**Credentials are resolved from the environment, never hardcoded and never logged**
— see `credentials`.(available?)Is a credential reachable? The gate a caller uses to decide between this backend and the stub.
Is a credential reachable? The gate a caller uses to decide between this backend and the stub.
(credentials)The credential to authenticate with, as {:kind :api-key|:bearer :value "…"}, or
nil when none is reachable.
Resolution order matches the official SDKs: ANTHROPIC_API_KEY, then
ANTHROPIC_AUTH_TOKEN, then an ant auth login profile via the ant CLI. An
unset ANTHROPIC_API_KEY does not mean there is no credential — an active OAuth
profile is enough, and it authenticates with Authorization: Bearer plus the OAuth
beta header rather than x-api-key.
The value is a secret: it is returned for the caller to hand straight to a header and is never logged, printed, or put in an exception message.
The credential to authenticate with, as `{:kind :api-key|:bearer :value "…"}`, or
nil when none is reachable.
Resolution order matches the official SDKs: `ANTHROPIC_API_KEY`, then
`ANTHROPIC_AUTH_TOKEN`, then an `ant auth login` profile via the `ant` CLI. **An
unset `ANTHROPIC_API_KEY` does not mean there is no credential** — an active OAuth
profile is enough, and it authenticates with `Authorization: Bearer` plus the OAuth
beta header rather than `x-api-key`.
The value is a secret: it is returned for the caller to hand straight to a header
and is never logged, printed, or put in an exception message.The model this backend talks to unless told otherwise.
The model this backend talks to unless told otherwise.
Beta flag for the scalar fallbacks: "default" form — routes a policy decline to
Anthropic's recommended substitute model by refusal category.
Beta flag for the scalar `fallbacks: "default"` form — routes a policy decline to Anthropic's recommended substitute model by refusal category.
(parse-response m)The API's JSON message -> the neutral response map (vaelii.impl.llm.protocol).
The API's JSON message -> the neutral response map (`vaelii.impl.llm.protocol`).
(provider)(provider {:keys [credential base-url timeout-ms] :or {timeout-ms 600000}})An Anthropic-backed Provider.
opts:
:credential {:kind :api-key|:bearer :value "…"} — resolved from the
environment by credentials when absent
:base-url default https://api.anthropic.com (or ANTHROPIC_BASE_URL)
:timeout-ms default 600000 — a high-effort turn on a hard task runs for minutes
Throws when no credential is reachable, so a caller that may run without one should
gate on available? and fall back to vaelii.impl.llm.stub/provider.
An Anthropic-backed `Provider`.
`opts`:
:credential `{:kind :api-key|:bearer :value "…"}` — resolved from the
environment by `credentials` when absent
:base-url default `https://api.anthropic.com` (or `ANTHROPIC_BASE_URL`)
:timeout-ms default 600000 — a high-effort turn on a hard task runs for minutes
Throws when no credential is reachable, so a caller that may run without one should
gate on `available?` and fall back to `vaelii.impl.llm.stub/provider`.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 |