Liking cljdoc? Tell your friends :D

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

build-ref-registryclj

(build-ref-registry spec-def)

Builds a registry of referenced specs from a spec's ::refs.

Recursively collects all refs from nested specs and returns a map of spec-name -> spec-def. Detects duplicate spec names and throws error.

Params: spec-def - Map. Spec definition with optional ::refs key.

Returns: Map. Registry mapping spec names (keywords) to spec definitions. Empty map if no refs.

Throws: ExceptionInfo if duplicate spec names are found.

Builds a registry of referenced specs from a spec's ::refs.

Recursively collects all refs from nested specs and returns a map
of spec-name -> spec-def. Detects duplicate spec names and throws error.

Params:
`spec-def` - Map. Spec definition with optional ::refs key.

Returns:
Map. Registry mapping spec names (keywords) to spec definitions.
Empty map if no refs.

Throws:
ExceptionInfo if duplicate spec names are found.
sourceraw docstring

coerce-data-with-specclj

(coerce-data-with-spec data spec-def)

Coerces Clojure data according to spec type definitions.

Unlike str->data-with-spec which parses JSON text, this operates on existing Clojure data structures (e.g., from SCI eval). Applies:

  1. Array normalization (vector wrapping a single object → unwrapped map)
  2. Keyword type coercion (strings → keywords for :spec.type/keyword fields)
  3. Spec field defaults (nil :many → [], missing keys → nil)

Does NOT apply ::key-ns namespacing — data keys are expected to already be in their final form. Designed for RLM answers where SCI produces Clojure data that needs type normalization but not JSON parsing or key restructuring.

Params: data - Clojure data (map, vector, or primitive) to coerce. spec-def - Map. Spec definition with ::fields, ::refs, etc.

Returns: Coerced data with keyword fields converted and defaults applied.

Coerces Clojure data according to spec type definitions.

Unlike str->data-with-spec which parses JSON text, this operates on existing
Clojure data structures (e.g., from SCI eval). Applies:
1. Array normalization (vector wrapping a single object → unwrapped map)
2. Keyword type coercion (strings → keywords for :spec.type/keyword fields)
3. Spec field defaults (nil :many → [], missing keys → nil)

Does NOT apply ::key-ns namespacing — data keys are expected to already
be in their final form. Designed for RLM answers where SCI produces Clojure
data that needs type normalization but not JSON parsing or key restructuring.

Params:
`data` - Clojure data (map, vector, or primitive) to coerce.
`spec-def` - Map. Spec definition with ::fields, ::refs, etc.

Returns:
Coerced data with keyword fields converted and defaults applied.
sourceraw docstring

data->strclj

(data->str data)

Serializes Clojure data to JSON string.

Converts dates/datetimes to ISO 8601 strings.

Params: data - Map. Clojure data structure with keyword keys.

Returns: String. JSON representation of the data.

Examples: (data->str {:name "John" :age 42}) => "{"name":"John","age":42}"

(data->str {:date (LocalDate/of 2024 1 15)}) => "{"date":"2024-01-15"}"

Serializes Clojure data to JSON string.

Converts dates/datetimes to ISO 8601 strings.

Params:
`data` - Map. Clojure data structure with keyword keys.

Returns:
String. JSON representation of the data.

Examples:
(data->str {:name "John" :age 42})
=> "{\"name\":\"John\",\"age\":42}"

(data->str {:date (LocalDate/of 2024 1 15)})
=> "{\"date\":\"2024-01-15\"}"
sourceraw docstring

fieldclj

(field &
       {the-description :com.blockether.svar.internal.spec/description
        the-name :com.blockether.svar.internal.spec/name
        :or {the-required true the-humanize? false}
        the-type :com.blockether.svar.internal.spec/type
        the-values :com.blockether.svar.internal.spec/values
        the-required :com.blockether.svar.internal.spec/required
        the-cardinality :com.blockether.svar.internal.spec/cardinality
        the-target :com.blockether.svar.internal.spec/target
        the-humanize? :com.blockether.svar.internal.spec/humanize?})

Defines a spec field with namespaced keyword options.

Params: ::name - Keyword, required. Field path/name as Datomic-style keyword. ::type - Keyword, required. One of:

  • :spec.type/string - String value
  • :spec.type/int - Integer value
  • :spec.type/float - Floating point value
  • :spec.type/bool - Boolean value
  • :spec.type/date - ISO date (YYYY-MM-DD)
  • :spec.type/datetime - ISO datetime
  • :spec.type/keyword - Clojure keyword (rendered as string, keywordized on parse)
  • :spec.type/ref - Reference to another spec
  • :spec.type/int-v-N - Fixed-size integer vector (e.g., :spec.type/int-v-4 for 4 ints)
  • :spec.type/string-v-N - Fixed-size string vector (e.g., :spec.type/string-v-2)
  • :spec.type/double-v-N - Fixed-size double vector (e.g., :spec.type/double-v-3) ::cardinality - Keyword, required.
  • :spec.cardinality/one - Single value
  • :spec.cardinality/many - Vector of values ::description - String, required. Human-readable description (no reserved chars). ::required - Boolean, optional. false if field can be nil (default: true). ::values - Map, optional. Enum constraint with value->description pairs. Every enum value MUST have a description explaining its meaning. Example: {"admin" "Full system access" "user" "Standard access"} ::target - Keyword or vector of keywords, optional. Required when ::type is :spec.type/ref. Single keyword for simple ref, vector for union types (e.g., [:Heading :Paragraph :Image]). ::humanize? - Boolean, optional. When true, marks this field for humanization when a :humanizer fn is passed to ask!. Defaults to false.

Returns: Map. Field definition with keys ::name, ::type, ::cardinality, ::description, and optionally ::union (for optional fields), ::values (for enums), ::target (for refs), and ::humanize? (for humanization marking).

Defines a spec field with namespaced keyword options.

Params:
`::name` - Keyword, required. Field path/name as Datomic-style keyword.
`::type` - Keyword, required. One of:
  - :spec.type/string - String value
  - :spec.type/int - Integer value
  - :spec.type/float - Floating point value
  - :spec.type/bool - Boolean value
  - :spec.type/date - ISO date (YYYY-MM-DD)
  - :spec.type/datetime - ISO datetime
  - :spec.type/keyword - Clojure keyword (rendered as string, keywordized on parse)
  - :spec.type/ref - Reference to another spec
  - :spec.type/int-v-N - Fixed-size integer vector (e.g., :spec.type/int-v-4 for 4 ints)
  - :spec.type/string-v-N - Fixed-size string vector (e.g., :spec.type/string-v-2)
  - :spec.type/double-v-N - Fixed-size double vector (e.g., :spec.type/double-v-3)
`::cardinality` - Keyword, required.
  - :spec.cardinality/one - Single value
  - :spec.cardinality/many - Vector of values
`::description` - String, required. Human-readable description (no reserved chars).
`::required` - Boolean, optional. false if field can be nil (default: true).
`::values` - Map, optional. Enum constraint with value->description pairs.
  Every enum value MUST have a description explaining its meaning.
  Example: {"admin" "Full system access" "user" "Standard access"}
`::target` - Keyword or vector of keywords, optional. Required when ::type is :spec.type/ref.
    Single keyword for simple ref, vector for union types (e.g., [:Heading :Paragraph :Image]).
 `::humanize?` - Boolean, optional. When true, marks this field for humanization
    when a :humanizer fn is passed to ask!. Defaults to false.
 
 Returns:
 Map. Field definition with keys ::name, ::type, ::cardinality, ::description,
 and optionally ::union (for optional fields), ::values (for enums), ::target (for refs),
 and ::humanize? (for humanization marking).
sourceraw docstring

specclj

(spec & args)

Creates a spec from field definitions.

Params: name - Keyword, optional. Name of the spec (e.g., :Person, :Address). opts - Map, optional. Options map that may contain: :refs - Vector of referenced specs for union types. ::key-ns - String. Namespace to add to all keys during parsing. Example: "page.node" transforms :type to :page.node/type. fields - Field definitions. Variadic list of field maps created with field.

Examples: (spec (field ...)) ; Anonymous spec (spec :Person (field ...)) ; Named spec (spec :Person {:refs [addr-spec]} (field ...)) ; Named spec with refs (spec :section {::key-ns "page.node"} (field ...)) ; Keys namespaced as :page.node/*

Returns: Map. Spec definition with ::fields key and optionally ::spec-name, ::refs, and ::key-ns keys.

Creates a spec from field definitions.

Params:
`name` - Keyword, optional. Name of the spec (e.g., :Person, :Address).
`opts` - Map, optional. Options map that may contain:
  `:refs` - Vector of referenced specs for union types.
  `::key-ns` - String. Namespace to add to all keys during parsing.
               Example: "page.node" transforms :type to :page.node/type.
`fields` - Field definitions. Variadic list of field maps created with `field`.

Examples:
(spec (field ...))                           ; Anonymous spec
(spec :Person (field ...))                   ; Named spec
(spec :Person {:refs [addr-spec]} (field ...)) ; Named spec with refs
(spec :section {::key-ns "page.node"} (field ...)) ; Keys namespaced as :page.node/*

Returns:
Map. Spec definition with ::fields key and optionally ::spec-name, ::refs, and ::key-ns keys.
sourceraw docstring

spec->promptclj

(spec->prompt the-spec)

Converts a spec to a full prompt for LLM with BAML-style schema.

Uses simple, direct format without XML tags.

Params: the-spec - Map. Spec definition created with spec.

Returns: String. Prompt with BAML-style schema.

Examples: "Answer in JSON using this schema:\n{ field: type, }"

Converts a spec to a full prompt for LLM with BAML-style schema.

Uses simple, direct format without XML tags.

Params:
`the-spec` - Map. Spec definition created with `spec`.

Returns:
String. Prompt with BAML-style schema.

Examples:
"Answer in JSON using this schema:\n{ field: type, }"
sourceraw docstring

str->dataclj

(str->data text)

Parses JSON response from LLM into Clojure data structure.

Uses jsonish parser to handle malformed JSON (unquoted keys/values, trailing commas, etc.).

Params: text - String. JSON response from LLM (may be malformed).

Returns: Map. Parsed Clojure data with keywords as keys, nested maps/vectors as values.

Examples: (str->data "{"name": "John", "age": 30}") => {:name "John", :age 30}

(str->data "{name: John, age: 30}") ; Unquoted - still works => {:name "John", :age 30}

Throws: RuntimeException if JSON cannot be parsed.

Parses JSON response from LLM into Clojure data structure.

Uses jsonish parser to handle malformed JSON (unquoted keys/values, trailing commas, etc.).

Params:
`text` - String. JSON response from LLM (may be malformed).

Returns:
Map. Parsed Clojure data with keywords as keys, nested maps/vectors as values.

Examples:
(str->data "{\"name\": \"John\", \"age\": 30}")
=> {:name "John", :age 30}

(str->data "{name: John, age: 30}")  ; Unquoted - still works
=> {:name "John", :age 30}

Throws:
RuntimeException if JSON cannot be parsed.
sourceraw docstring

str->data-with-specclj

(str->data-with-spec text spec-def)

Parses JSON response from LLM into Clojure data structure with spec-aware processing.

Uses jsonish parser to handle malformed JSON, then:

  1. Remaps keys to match original spec field names (preserves ?!*+ chars)
  2. Converts :spec.type/keyword fields from strings to Clojure keywords
  3. Applies ::key-ns namespace to keys if configured in spec

Params: text - String. JSON response from LLM (may be malformed). spec-def - Map. Spec definition used to generate the prompt.

Returns: Map. Parsed Clojure data with keys matching original spec field names, keyword-typed fields converted to Clojure keywords, and keys namespaced if ::key-ns is configured.

Examples: (def my-spec (spec (field ::name :valid? ::type :spec.type/bool ...))) (str->data-with-spec "{"valid": true}" my-spec) => {:valid? true} ; Note: key is :valid? not :valid

(def kw-spec (spec (field ::name :status ::type :spec.type/keyword ...))) (str->data-with-spec "{"status": "active"}" kw-spec) => {:status :active} ; String converted to keyword

(def ns-spec (spec :node {::key-ns "page.node"} (field ::name :type ...))) (str->data-with-spec "{"type": "heading"}" ns-spec) => {:page.node/type "heading"} ; Key namespaced

Throws: RuntimeException if JSON cannot be parsed.

Parses JSON response from LLM into Clojure data structure with spec-aware processing.

Uses jsonish parser to handle malformed JSON, then:
1. Remaps keys to match original spec field names (preserves ?!*+ chars)
2. Converts :spec.type/keyword fields from strings to Clojure keywords
3. Applies ::key-ns namespace to keys if configured in spec

Params:
`text` - String. JSON response from LLM (may be malformed).
`spec-def` - Map. Spec definition used to generate the prompt.

Returns:
Map. Parsed Clojure data with keys matching original spec field names,
keyword-typed fields converted to Clojure keywords, and keys namespaced
if ::key-ns is configured.

Examples:
(def my-spec (spec (field ::name :valid? ::type :spec.type/bool ...)))
(str->data-with-spec "{\"valid\": true}" my-spec)
=> {:valid? true}  ; Note: key is :valid? not :valid

(def kw-spec (spec (field ::name :status ::type :spec.type/keyword ...)))
(str->data-with-spec "{\"status\": \"active\"}" kw-spec)
=> {:status :active}  ; String converted to keyword

(def ns-spec (spec :node {::key-ns "page.node"} (field ::name :type ...)))
(str->data-with-spec "{\"type\": \"heading\"}" ns-spec)
=> {:page.node/type "heading"}  ; Key namespaced

Throws:
RuntimeException if JSON cannot be parsed.
sourceraw docstring

validate-dataclj

(validate-data the-spec data)

Validates parsed data against a spec. Returns {:valid? true} if valid, or {:valid? false :errors [...]} with error details.

Handles array of objects pattern where a field with cardinality many contains objects with nested fields (e.g., :books with :books.title, :books.year).

Checks:

  • Required fields are present (unless ::union contains ::nil)
  • Values match expected types
  • Enum values are valid (if ::values is specified)

Params: the-spec - Spec definition (created with spec/spec and spec/field) data - Parsed Clojure data to validate

Returns: Map with :valid? boolean and optional :errors vector of error maps.

Validates parsed data against a spec.
Returns {:valid? true} if valid, or {:valid? false :errors [...]} with error details.

Handles array of objects pattern where a field with cardinality many contains
objects with nested fields (e.g., :books with :books.title, :books.year).

Checks:
- Required fields are present (unless ::union contains ::nil)
- Values match expected types
- Enum values are valid (if ::values is specified)

Params:
`the-spec` - Spec definition (created with spec/spec and spec/field)
`data` - Parsed Clojure data to validate

Returns:
Map with :valid? boolean and optional :errors vector of error maps.
sourceraw 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