Liking cljdoc? Tell your friends :D

com.blockether.svar.core

LLM interaction utilities for structured and unstructured outputs.

SVAR = Structured Validated Automated Reasoning

Scope: structured LLM output + provider routing. Main functions:

  • ask! - Structured output using the spec DSL
  • ask-code! - native tool-calling completion (the model acts via tools)
  • models! - Fetch available models from the LLM API

Re-exports the spec DSL (field, spec, str->data, str->data-with-spec, data->str, validate-data, spec->prompt, build-ref-registry) and make-router so users can require only this namespace.

Configuration: LLM calls route automatically via the router.

Example: (ask! router {:spec my-spec :messages [(system "Help the user.") (user "What is 2+2?")] :model "gpt-4o"})

LLM interaction utilities for structured and unstructured outputs.

SVAR = Structured Validated Automated Reasoning

 Scope: structured LLM output + provider routing. Main functions:
 - `ask!` - Structured output using the spec DSL
 - `ask-code!` - native tool-calling completion (the model acts via tools)
 - `models!` - Fetch available models from the LLM API

  Re-exports the spec DSL (`field`, `spec`, `str->data`, `str->data-with-spec`,
  `data->str`, `validate-data`, `spec->prompt`, `build-ref-registry`) and
  `make-router` so users can require only this namespace.

Configuration:
LLM calls route automatically via the router.

 Example:
  (ask! router {:spec my-spec
                :messages [(system "Help the user.")
                           (user "What is 2+2?")]
                :model "gpt-4o"})
raw docstring

com.blockether.svar.extension

Extension-facing helpers for router limits and parse diagnostics.

Preserved-thinking handoff is provider-agnostic via the :assistant-message field on ask! / ask-code! results. The value is a canonical svar message — {:role "assistant" :content [<canonical-blocks>]} — which callers append to :messages on the next call. Canonical {:type "thinking"} content blocks carry the per-provider preserved-reasoning state under :thinking-signature; svar's wire serializers transform them into native shapes (Anthropic signed thinking blocks, z.ai reasoning_content field, OpenAI Responses reasoning input items). Plain chat models without preserved thinking just don't surface :assistant-message, so the same caller pipeline (keep :assistant-message results) works uniformly across every provider with zero per-provider branching.

Extension-facing helpers for router limits and parse diagnostics.

Preserved-thinking handoff is provider-agnostic via the
`:assistant-message` field on `ask!` / `ask-code!` results. The
value is a canonical svar message — `{:role "assistant" :content
[<canonical-blocks>]}` — which callers append to `:messages` on
the next call. Canonical `{:type "thinking"}` content blocks
carry the per-provider preserved-reasoning state under
`:thinking-signature`; svar's wire serializers transform them into
native shapes (Anthropic signed thinking blocks, z.ai
`reasoning_content` field, OpenAI Responses reasoning input items).
Plain chat models without preserved thinking just don't surface
`:assistant-message`, so the same caller pipeline
`(keep :assistant-message results)` works uniformly across every
provider with zero per-provider branching.
raw docstring

com.blockether.svar.internal.jsonish

Wrapper for the JsonishParser Java class.

Provides SAP (Schemaless Adaptive Parsing) for malformed JSON from LLMs. Handles unquoted keys/values, trailing commas, markdown code blocks, etc.

Wrapper for the JsonishParser Java class.

Provides SAP (Schemaless Adaptive Parsing) for malformed JSON from LLMs.
Handles unquoted keys/values, trailing commas, markdown code blocks, etc.
raw docstring

com.blockether.svar.internal.llm

LLM client layer: HTTP transport, message construction, and all LLM interaction functions (ask!, abstract!, eval!, refine!, models!, sample!).

LLM client layer: HTTP transport, message construction, and all LLM interaction
functions (ask!, abstract!, eval!, refine!, models!, sample!).
raw docstring

com.blockether.svar.internal.modelsdev

models.dev catalog loader.

Reads the bundled resources/models.dev.json snapshot (refreshed via make refresh-models) and exposes a normalized view that downstream router code merges with KNOWN_PROVIDERS wire/policy overlay.

Catalog wins for: pricing, context, modalities, capability flags, release dates, family. svar overlay wins for: api-style, reasoning-style, llm-headers, env-keys, base-url, paths, extra-body, exclude-models, rate budgets, default-models.

Plan-vs-retail pricing — per-provider entries on models.dev already reflect plan zeros (e.g. github-copilot, zai-coding-plan ship {input:0, output:0}). For svar's :openai-codex and :anthropic-coding-plan we explicitly want retail pricing (the user pays at API rates once metered), so the overlay declares :pricing-source to redirect catalog lookup to the retail provider.

models.dev catalog loader.

Reads the bundled `resources/models.dev.json` snapshot (refreshed via
`make refresh-models`) and exposes a normalized view that downstream
router code merges with `KNOWN_PROVIDERS` wire/policy overlay.

Catalog wins for: pricing, context, modalities, capability flags,
release dates, family.
svar overlay wins for: api-style, reasoning-style, llm-headers,
env-keys, base-url, paths, extra-body, exclude-models, rate budgets,
default-models.

Plan-vs-retail pricing — per-provider entries on models.dev already
reflect plan zeros (e.g. `github-copilot`, `zai-coding-plan` ship
{input:0, output:0}). For svar's `:openai-codex` and
`:anthropic-coding-plan` we explicitly want **retail** pricing
(the user pays at API rates once metered), so the overlay declares
`:pricing-source` to redirect catalog lookup to the retail provider.
raw docstring

com.blockether.svar.internal.spec

Structured output specification system for LLM responses.

This namespace provides a DSL for defining expected output structures, converting specs to LLM prompts, and parsing LLM responses back to Clojure data.

Primary functions:

  • field - Define a field with name, type, cardinality, and description
  • spec - Create a spec from field definitions
  • build-ref-registry - Build a registry of referenced specs for nested types
  • spec->prompt - Generate LLM prompt text from a spec (sent to LLM)
  • str->data - Parse LLM response string to Clojure data (schemaless)
  • str->data-with-spec - Parse LLM response with spec-based type coercion
  • validate-data - Validate parsed data against a spec
  • data->str - Serialize Clojure data to JSON string

Data Flow:

  1. Define spec with spec and field functions
  2. Generate prompt with spec->prompt (sent to LLM)
  3. Parse response with str->data-with-spec (LLM response -> typed Clojure map)
  4. Optionally validate with validate-data
  5. Optionally serialize with data->str
Structured output specification system for LLM responses.

This namespace provides a DSL for defining expected output structures,
converting specs to LLM prompts, and parsing LLM responses back to Clojure data.

Primary functions:
- `field` - Define a field with name, type, cardinality, and description
- `spec` - Create a spec from field definitions
- `build-ref-registry` - Build a registry of referenced specs for nested types
- `spec->prompt` - Generate LLM prompt text from a spec (sent to LLM)
- `str->data` - Parse LLM response string to Clojure data (schemaless)
- `str->data-with-spec` - Parse LLM response with spec-based type coercion
- `validate-data` - Validate parsed data against a spec
- `data->str` - Serialize Clojure data to JSON string

Data Flow:
1. Define spec with `spec` and `field` functions
2. Generate prompt with `spec->prompt` (sent to LLM)
3. Parse response with `str->data-with-spec` (LLM response -> typed Clojure map)
4. Optionally validate with `validate-data`
5. Optionally serialize with `data->str`
raw docstring

com.blockether.svar.internal.usage

Canonical token-usage shape — single source of truth across providers.

Phase A of svar 0.6.0. Replaces the hybrid pre-0.6 shape that emitted :prompt_tokens with provider-dependent semantics (Anthropic additive, OpenAI inclusive) under the same key. Every provider normalizer now produces the SAME shape; downstream consumers read one set of keys regardless of which model served the call.

Canonical shape — INVARIANT: regular + cache-write + cache-read = input-tokens:

{:input-tokens <long> ;; TOTAL prompt tokens (always inclusive) :output-tokens <long> ;; TOTAL completion tokens :input-tokens-details {:regular <long> ;; not from cache, not written :cache-write <long> ;; written this request (1.25× input rate, anthropic; 0 else) :cache-read <long>} ;; served from cache (0.1× input rate, anthropic; ~10-50% off, openai) :output-tokens-details {:reasoning <long>} ;; subset of output-tokens :total-tokens <long> ;; convenience = input-tokens + output-tokens :raw <map>} ;; original provider envelope (debug / forensics)

Provider differences:

  • Anthropic Messages API (:anthropic api-style): RAW input_tokens excludes cached AND cache-creation. Canonical :input-tokens adds all three so the value is TOTAL.

  • OpenAI Chat / Responses (:openai-compatible-* api-styles): RAW prompt_tokens / input_tokens IS the total. Cached subset lives under prompt_tokens_details.cached_tokens / input_tokens_details.cached_tokens. No native cache-write concept (server-managed implicit caching), so :cache-write is always 0 here UNLESS the provider proxies Anthropic via OpenRouter and surfaces cache_creation_input_tokens as a pydantic extra field.

  • Z.ai (GLM coding-plan / OpenAI-compatible-chat): same as OpenAI.

Industry alignment (May 2026):

  • Vercel AI SDK V3 spec (vercel/ai#9921): inputTokens always TOTAL with inputTokensDetails {regular, cacheWrite, cacheRead}.
  • OpenTelemetry gen_ai.usage.input_tokens (≥ v1.37): SHOULD be inclusive (all kinds of input tokens).
  • Claude Code official statusline JSON: context_window.total_input_tokens = input + cache_creation + cache_read.

Reject: the additive convention (e.g. litellm PR #23342) leaves total_tokens inconsistent with prompt_tokens and breaks naive aggregation downstream.

Canonical token-usage shape — single source of truth across providers.

Phase A of svar 0.6.0. Replaces the hybrid pre-0.6 shape that emitted
`:prompt_tokens` with provider-dependent semantics (Anthropic
additive, OpenAI inclusive) under the same key. Every provider
normalizer now produces the SAME shape; downstream consumers read
one set of keys regardless of which model served the call.

Canonical shape — INVARIANT: `regular + cache-write + cache-read = input-tokens`:

  {:input-tokens          <long>   ;; TOTAL prompt tokens (always inclusive)
   :output-tokens         <long>   ;; TOTAL completion tokens
   :input-tokens-details  {:regular     <long>   ;; not from cache, not written
                           :cache-write <long>   ;; written this request (1.25× input rate, anthropic; 0 else)
                           :cache-read  <long>}  ;; served from cache (0.1× input rate, anthropic; ~10-50% off, openai)
   :output-tokens-details {:reasoning   <long>}  ;; subset of output-tokens
   :total-tokens          <long>   ;; convenience = input-tokens + output-tokens
   :raw                   <map>}   ;; original provider envelope (debug / forensics)

Provider differences:

- Anthropic Messages API (`:anthropic` api-style): RAW
  `input_tokens` excludes cached AND cache-creation. Canonical
  `:input-tokens` adds all three so the value is TOTAL.

- OpenAI Chat / Responses (`:openai-compatible-*` api-styles): RAW
  `prompt_tokens` / `input_tokens` IS the total. Cached subset lives
  under `prompt_tokens_details.cached_tokens` /
  `input_tokens_details.cached_tokens`. No native `cache-write`
  concept (server-managed implicit caching), so `:cache-write` is
  always 0 here UNLESS the provider proxies Anthropic via OpenRouter
  and surfaces `cache_creation_input_tokens` as a pydantic extra
  field.

- Z.ai (GLM coding-plan / OpenAI-compatible-chat): same as OpenAI.

Industry alignment (May 2026):

- Vercel AI SDK V3 spec (vercel/ai#9921): `inputTokens` always TOTAL
  with `inputTokensDetails {regular, cacheWrite, cacheRead}`.
- OpenTelemetry `gen_ai.usage.input_tokens` (≥ v1.37): SHOULD be
  inclusive (all kinds of input tokens).
- Claude Code official statusline JSON:
  `context_window.total_input_tokens = input + cache_creation + cache_read`.

Reject: the additive convention (e.g. litellm PR #23342) leaves
`total_tokens` inconsistent with `prompt_tokens` and breaks naive
aggregation downstream.
raw docstring

com.blockether.svar.internal.util

Shared internal utilities.

Shared internal utilities.
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close