Liking cljdoc? Tell your friends :D

vaelii.impl.llm.text

The document-scoped prompt: text in, candidates out — never knowledge in.

vaelii.impl.gloss composes English out of the KB and says why that direction is the dangerous one: nothing in the engine can check that a sentence means what a text said. This namespace is the other direction, and it has to answer that argument rather than ignore it, because every check the engine has passes on a correct-looking translation of the wrong claim — naming, well-formedness, argument types and disjointness all read the sentence. So what is built here is a candidate generator with a reviewer between it and the store, and every decision below follows from that:

  • a candidate is a [sentence context opts] entry, which is the shape vaelii.core/edit already takes and the browser's editor already parses — so a proposal lands as a reviewable diff and there is no second write path;
  • a candidate carries the span of the text it came from, so an accepted sentence is auditable back to the sentence that produced it;
  • a candidate is :default, never :monotonic unless the reader says so — a translated guess asserted as known-true would defeat hand-written defaults;
  • what the pipeline could not translate is part of the answer (coverage), because a reader who is shown only the two-thirds that worked reads it as a reader that understood the document.

Where the boundary is. Nothing here is in vaelii.core, and nothing here writes. Like web / serve / llm, this is an application over the engine: the engine's contribution is the critic (check-edit), the vocabulary (the taxonomy and the declarations), the provenance side map, and the equality partition — all of them public reads that existed already. See docs/reading.md.

Resolution is the problem; parsing is not

A pipeline that coins has_black_and_white_feathers for every sentence produces fragmentation rather than knowledge, and no naming check refuses it (docs/naming.md says so in as many words). So the document's own words are resolved against the vocabulary the KB already has before a model is asked anything:

  • spellings turns each run of the document's words into the symbols a KB term could be spelled as — prepared for winter into preparedForWinter and prepared_for_winter, Fido into Fido — and known asks the KB which of them it has. Generating and asking runs the opposite way from inverting the KB's vocabulary into the words each term is written with, which is the one read here that would grow with the KB;
  • resolve-in walks the document longest-run-first and non-overlapping, so a compound predicate is not shredded into the words its name is made of;
  • every resolved term is the equality partition's representative, so a word spelled at a retired name resolves to the term that name was merged into.

What resolves becomes the vocabulary card (document-inventory), which is the prevention half of the fragmentation guard; the detection half is vaelii.impl.llm.inventory/coined, unchanged and shared with the other three paths.

The document-scoped prompt: **text in, candidates out — never knowledge in.**

`vaelii.impl.gloss` composes English *out of* the KB and says why that direction is the
dangerous one: nothing in the engine can check that a sentence means what a text said.
This namespace is the other direction, and it has to answer that argument rather than
ignore it, because every check the engine has passes on a correct-looking translation of
the wrong claim — naming, well-formedness, argument types and disjointness all read the
*sentence*.  So what is built here is a **candidate generator with a reviewer between it
and the store**, and every decision below follows from that:

* a candidate is a `[sentence context opts]` entry, which is the shape
  `vaelii.core/edit` already takes and the browser's editor already parses — so a
  proposal lands as a reviewable diff and there is no second write path;
* a candidate carries the **span** of the text it came from, so an accepted sentence is
  auditable back to the sentence that produced it;
* a candidate is `:default`, never `:monotonic` unless the reader says so — a translated
  guess asserted as known-true would defeat hand-written defaults;
* what the pipeline **could not** translate is part of the answer (`coverage`), because a
  reader who is shown only the two-thirds that worked reads it as a reader that
  understood the document.

**Where the boundary is.**  Nothing here is in `vaelii.core`, and nothing here writes.
Like `web` / `serve` / `llm`, this is an application over the engine: the engine's
contribution is the critic (`check-edit`), the vocabulary (the taxonomy and the
declarations), the provenance side map, and the equality partition — all of them public
reads that existed already.  See docs/reading.md.

## Resolution is the problem; parsing is not

A pipeline that coins `has_black_and_white_feathers` for every sentence produces
fragmentation rather than knowledge, and no naming check refuses it (docs/naming.md says
so in as many words).  So the document's own words are resolved against the vocabulary
the KB already has **before** a model is asked anything:

* `spellings` turns each run of the document's words into the symbols a KB term could be
  spelled as — *prepared for winter* into `preparedForWinter` and `prepared_for_winter`,
  *Fido* into `Fido` — and `known` asks the KB which of them it has.  Generating and
  asking runs the *opposite* way from inverting the KB's vocabulary into the words each
  term is written with, which is the one read here that would grow with the KB;
* `resolve-in` walks the document longest-run-first and non-overlapping, so a compound
  predicate is not shredded into the words its name is made of;
* every resolved term is the equality partition's `representative`, so a word spelled at
  a retired name resolves to the term that name was merged into.

What resolves becomes the vocabulary card (`document-inventory`), which is the
prevention half of the fragmentation guard; the detection half is
`vaelii.impl.llm.inventory/coined`, unchanged and shared with the other three paths.
raw docstring

candidate-entryclj

(candidate-entry {:keys [sentence segment confidence strength]}
                 context
                 source
                 segs)

One read candidate -> the [sentence context opts] entry vaelii.core/edit takes.

opts carries the provenance{:source :segment :span :confidence} — which is the reason to prefer this over a script that pastes facts in: the per-handle provenance map is open and unread by belief (docs/storage.md), so an accepted sentence stays auditable back to the characters it came from. A candidate whose segment names no sentence gets no span and says so with :segment nil, rather than being given a plausible one.

:strength is omitted unless the candidate claimed :monotonic, so a candidate is :default by construction: a translated guess that defeated a hand-written default would be the worst outcome this pipeline has available.

One read candidate -> the `[sentence context opts]` entry `vaelii.core/edit` takes.

`opts` carries the **provenance** — `{:source :segment :span :confidence}` — which is the
reason to prefer this over a script that pastes facts in: the per-handle provenance map
is open and unread by belief (docs/storage.md), so an accepted sentence stays auditable
back to the characters it came from.  A candidate whose `segment` names no sentence gets
no span and says so with `:segment nil`, rather than being given a plausible one.

`:strength` is omitted unless the candidate claimed `:monotonic`, so a candidate is
`:default` by construction: a translated guess that defeated a hand-written default
would be the worst outcome this pipeline has available.
raw docstring

confidence-ranksclj

The three tiers a candidate can claim, as the number provenance records.

A rank, not a probability: it orders a review queue and nothing else reads it. A number rather than the keyword because ordering is the whole job, and belief never reads provenance at all — so this cannot become a defeat class even by accident. There are two strength classes and there will not be a third (docs/nmtms.md); a parser's confidence lives here instead.

The three tiers a candidate can claim, as the number provenance records.

A **rank, not a probability**: it orders a review queue and nothing else reads it.  A
number rather than the keyword because ordering is the whole job, and belief never reads
provenance at all — so this cannot become a defeat class even by accident.  There are two
strength classes and there will not be a third (docs/nmtms.md); a parser's confidence
lives here instead.
raw docstring

coverageclj

(coverage segs candidates untranslated)

Which of the document's sentences the reader translated, and which it did not:

{:segments 5 :covered 4
 :uncovered [{:index 1 :span [42 61] :text "It was a fine morning."
              :reason "scene-setting, no claim"}]}

Computed, not reported. A segment is uncovered when no candidate names it, whatever the model listed in untranslated — a model that quietly drops a third of a document and says nothing would otherwise read as one that understood it. A stated reason is attached where there is one and the segment really did produce nothing; where the model claimed a sentence was untranslatable and produced a candidate from it, the candidate wins and the claim is dropped.

Taken over the candidates as read, not over the ones that survived. A sentence the reader turned into something the KB already stores, or into something the critic refused, was still read — reporting it as untranslated would blame the reader for the winnowing, and coverage is a claim about the document, not about the batch.

This is part of the default answer rather than something a caller asks for, which is the whole point of it.

Which of the document's sentences the reader translated, and which it did not:

    {:segments 5 :covered 4
     :uncovered [{:index 1 :span [42 61] :text "It was a fine morning."
                  :reason "scene-setting, no claim"}]}

**Computed, not reported.**  A segment is uncovered when no candidate names it, whatever
the model listed in `untranslated` — a model that quietly drops a third of a document and
says nothing would otherwise read as one that understood it.  A stated reason is attached
where there is one and the segment really did produce nothing; where the model claimed a
sentence was untranslatable *and* produced a candidate from it, the candidate wins and the
claim is dropped.

Taken over the candidates **as read**, not over the ones that survived.  A sentence the
reader turned into something the KB already stores, or into something the critic refused,
was still read — reporting it as untranslated would blame the reader for the winnowing,
and coverage is a claim about the *document*, not about the batch.

This is part of the default answer rather than something a caller asks for, which is the
whole point of it.
raw docstring

declared-inclj

(declared-in kb context)

Every predicate declared in context's cone, as [[predicate arity] …] — its unaryPredicate / binaryPredicate / ternaryPredicate memberships, read at that context so the answer is what a sentex filed there would be allowed to use.

Scoped where vaelii.impl.llm.inventory/declared-arities is not, and that is the difference that matters here: a candidate is filed in one context, and the vocabulary of the microtheory it lands in is precisely the vocabulary it may reuse. What this puts on the card is names and shapes, never content — a predicate's declaration says it exists and takes two arguments, and says nothing about what is true of anything.

Ordered nearest microtheory first, because that is where a cap has to cut. The cone of a leaf context runs from the story it is about up to the vocabulary head, and a predicate the story's own theory declares is worth more to a reader of that story than one CoreContext declares — so the cone is sorted by how much each context sees, which is largest at the leaf and smallest at the head. Alphabetical within one context, so the order is a function of the taxonomy and never of arrival.

The cone is walked term by term because a read at a ground context is exact-context: sentexes-matching at LionMouseContext answers about that context alone, and what the context sees is context-up, a cached closure lookup. So this is one narrow read per context in the cone.

Every predicate declared **in `context`'s cone**, as `[[predicate arity] …]` — its
`unaryPredicate` / `binaryPredicate` / `ternaryPredicate` memberships, read at that
context so the answer is what a sentex filed there would be allowed to use.

Scoped where `vaelii.impl.llm.inventory/declared-arities` is not, and that is the
difference that matters here: a candidate is filed in one context, and the vocabulary of
the microtheory it lands in is precisely the vocabulary it may reuse.  What this puts on
the card is **names and shapes**, never content — a predicate's declaration says it exists
and takes two arguments, and says nothing about what is true of anything.

**Ordered nearest microtheory first**, because that is where a cap has to cut.  The cone
of a leaf context runs from the story it is about up to the vocabulary head, and a
predicate the story's own theory declares is worth more to a reader of that story than
one `CoreContext` declares — so the cone is sorted by how much each context sees, which
is largest at the leaf and smallest at the head.  Alphabetical within one context, so the
order is a function of the taxonomy and never of arrival.

The cone is walked term by term because a read at a *ground* context is exact-context:
`sentexes-matching` at `LionMouseContext` answers about that context alone, and what the
context *sees* is `context-up`, a cached closure lookup.  So this is one narrow read per
context in the cone.
raw docstring

document-inventoryclj

(document-inventory kb resolved context)
(document-inventory kb
                    resolved
                    context
                    {:keys [max-relations max-types max-genls]
                     :or {max-relations 60 max-types 60 max-genls 4}})

The vocabulary a document should be written in, in vaelii.impl.llm.inventory/inventory's shape so its renderer takes it unchanged: {:types :relations :structural :dropped}.

Three relevance tiers, in the order a token cap should cut them from the bottom:

  1. what the document's own words resolved to — the vocabulary the text demonstrably wants, and the reason resolution runs before the model is asked anything;
  2. what an argIsa licenses for a resolved type, so a document about a mouse is offered the relations a mouse can stand in;
  3. everything else context declares, nearest microtheory first (declared-in) — the vocabulary of the theory the candidates land in. Last because it is the least targeted, and included at all because a document rarely spells a predicate the way the KB does: repay the kindness does not resolve repaidKindness, and a reader who cannot see that name coins a synonym for it.

The type block is the resolved types plus one step up the taxonomy, so mouse puts animal on the card and the block reads as a hierarchy.

Cost is the vocabulary's, not the document's and not the KB's — the same shape vaelii.impl.llm.inventory/inventory has, and the same reason: the taxonomy's node set, the arity declarations, one comment read per rendered term, and one narrow argIsa query per resolved type and per rendered predicate. Nothing here walks facts. The card tracks the document (tiers 1 and 2 are seeded by what resolved); the reads track how much vocabulary the KB has.

opts: :max-relations (60), :max-types (60), :max-genls (4). Smaller than the page path's caps by design: a document resolves many terms where a page has one, and the window belongs to the text.

The vocabulary a document should be written in, in
`vaelii.impl.llm.inventory/inventory`'s shape so its renderer takes it unchanged:
`{:types :relations :structural :dropped}`.

Three relevance tiers, in the order a token cap should cut them from the bottom:

1. what the document's **own words** resolved to — the vocabulary the text demonstrably
   wants, and the reason resolution runs before the model is asked anything;
2. what an `argIsa` **licenses** for a resolved type, so a document about a mouse is
   offered the relations a mouse can stand in;
3. everything else `context` **declares**, nearest microtheory first (`declared-in`) —
   the vocabulary of the theory the candidates land in.  Last because it is the least
   targeted, and included at all because a document rarely spells a predicate the way the
   KB does: *repay the kindness* does not resolve `repaidKindness`, and a reader who
   cannot see that name coins a synonym for it.

The type block is the resolved types plus one step up the taxonomy, so `mouse` puts
`animal` on the card and the block reads as a hierarchy.

**Cost is the vocabulary's, not the document's and not the KB's** — the same shape
`vaelii.impl.llm.inventory/inventory` has, and the same reason: the taxonomy's node set,
the arity declarations, one `comment` read per rendered term, and one narrow `argIsa`
query per resolved type and per rendered predicate.  Nothing here walks facts.  The
*card* tracks the document (tiers 1 and 2 are seeded by what resolved); the *reads* track
how much vocabulary the KB has.

`opts`: `:max-relations` (60), `:max-types` (60), `:max-genls` (4).  Smaller than the page
path's caps by design: a document resolves many terms where a page has one, and the window
belongs to the text.
raw docstring

entry-provenanceclj

(entry-provenance entry)

The provenance map on an entry built by candidate-entry, or nil.

The provenance map on an entry built by `candidate-entry`, or nil.
raw docstring

knownclj

(known kb spellings)

The generated spellings the KB actually has a term for, as {spelling representative}.

One roster read, not a probe apiece. vaelii.impl.llm.inventory/unknown-terms asks the index's O(1) roots first because it is asked about a proposal, where nearly every functor is already known; a document is the opposite case — a hundred words generate hundreds of spellings and almost none of them name anything — so the roots would be probed hundreds of times to answer no, where the roster answers all of them at once.

A term is kept only if its spelling gives it a role a candidate could use: a context is dropped (candidates are filed in the caller's context, never one the text names) and so is the structural frame. Every value is the equality partition's representative, so a word spelled at a retired name resolves to the term that name was merged into.

The generated spellings the KB actually has a term for, as `{spelling representative}`.

**One roster read, not a probe apiece.**  `vaelii.impl.llm.inventory/unknown-terms` asks
the index's O(1) roots first because it is asked about a *proposal*, where nearly every
functor is already known; a document is the opposite case — a hundred words generate
hundreds of spellings and almost none of them name anything — so the roots would be
probed hundreds of times to answer no, where the roster answers all of them at once.

A term is kept only if its spelling gives it a role a candidate could use: a context is
dropped (candidates are filed in the caller's context, never one the text names) and so is
the structural frame.  Every value is the equality partition's `representative`, so a word
spelled at a retired name resolves to the term that name was merged into.
raw docstring

numberedclj

(numbered segs)

The document as the prompt shows it: one numbered line per segment. Numbering is what the segment field on every candidate refers back to, so it is the whole of how a span survives the round trip through a model.

The document as the prompt shows it: one numbered line per segment.  Numbering is what
the `segment` field on every candidate refers back to, so it is the whole of how a span
survives the round trip through a model.
raw docstring

output-schemaclj

The JSON schema decoding is constrained to — the contract on this path, for the page path's measured reason: on generation a schema is what makes a local model answer in s-expressions at all rather than in an essay.

Two fields do work no other path needs. segment is the index of the sentence the candidate came from, and it is how the span reaches provenance — the model is the only thing that knows which sentence it was translating, and asking for the offsets instead would be asking it to count characters. untranslated is where a sentence it could not formalize goes, with a reason; the coverage report does not trust it (a segment that produced no candidate is uncovered whether the model said so or not), but a stated reason is worth more to a reviewer than an inferred silence.

The JSON schema decoding is constrained to — **the contract on this path**, for the page
path's measured reason: on generation a schema is what makes a local model answer in
s-expressions at all rather than in an essay.

Two fields do work no other path needs.  **`segment`** is the index of the sentence the
candidate came from, and it is how the span reaches provenance — the model is the only
thing that knows which sentence it was translating, and asking for the offsets instead
would be asking it to count characters.  **`untranslated`** is where a sentence it could
not formalize goes, with a reason; the coverage report does not trust it (a segment that
produced no candidate is uncovered whether the model said so or not), but a stated reason
is worth more to a reviewer than an inferred silence.
raw docstring

resolutionsclj

(resolutions kb segs)

Every term the document's own words already name, in document order.

This is the whole of what the pipeline knows about the text before a model is asked anything, and it is what the vocabulary card is built from — the prevention half of the fragmentation guard. Two passes over the document and one KB read: every spelling the document could contain is generated first, known answers all of them together, and the walk is then arithmetic.

Every term the document's own words already name, in document order.

This is the whole of what the pipeline knows about the text before a model is asked
anything, and it is what the vocabulary card is built from — the prevention half of the
fragmentation guard.  Two passes over the document and **one** KB read: every spelling the
document could contain is generated first, `known` answers all of them together, and the
walk is then arithmetic.
raw docstring

resolve-inclj

(resolve-in known segs)

The resolutions in segs against an already-computed known map, as [{:surface "prepared for winter" :term preparedForWinter :segment 0 :span [12 31]} …].

Longest run first and non-overlapping: prepared for winter wins over winter, so a compound predicate is not shredded into the words its name is made of. Within one length the spelling order of spellings decides, which puts the predicate reading ahead of the type reading — the same precedence the naming invariants give a bare lowercase word.

Separate from resolutions so the walk is testable without a KB: everything here is arithmetic over the segments and the map.

The resolutions in `segs` against an already-computed `known` map, as
`[{:surface "prepared for winter" :term preparedForWinter :segment 0 :span [12 31]} …]`.

Longest run first and **non-overlapping**: *prepared for winter* wins over *winter*, so a
compound predicate is not shredded into the words its name is made of.  Within one length
the spelling order of `spellings` decides, which puts the predicate reading ahead of the
type reading — the same precedence the naming invariants give a bare lowercase word.

Separate from `resolutions` so the walk is testable without a KB: everything here is
arithmetic over the segments and the map.
raw docstring

review-queueclj

(review-queue entries flagged)

The candidates in the order a reviewer should read them: flagged first, then least confident first, then document order.

flagged is the set of entry indices something has a reservation about — a coined functor (vaelii.impl.llm.inventory/coined) or a shape vaelii.impl.llm.correct would rewrite. Both are failures the check chain admits by design and only a person can settle, so they sort to the top whatever the candidate claims about itself; confidence is the parser's own account of how much of the claim was reading and how much was guessing, and it breaks the tie — which is all a confidence is for here.

The candidates in the order a reviewer should read them: **flagged first**, then least
confident first, then document order.

`flagged` is the set of entry indices something has a reservation about — a coined functor
(`vaelii.impl.llm.inventory/coined`) or a shape `vaelii.impl.llm.correct` would rewrite.
Both are failures the check chain admits *by design* and only a person can settle, so they
sort to the top whatever the candidate claims about itself; confidence is the parser's own
account of how much of the claim was reading and how much was guessing, and it breaks the
tie — which is all a confidence is for here.
raw docstring

segmentsclj

(segments doc)

A document cut into sentences: [{:index i :text "…" :span [start end]} …].

The span is what everything downstream is for — (subs doc start end) is the segment's text, exactly, so an accepted candidate points back at characters rather than at a paraphrase. A test holds that identity.

Cut at a run of ./!/? followed by whitespace or the end of the document, which is crude and deliberately so: an abbreviation splits a segment in two, which costs a narrower span and no correctness, while a cleverer splitter would need a model and put one on the reading side of the pipeline where there is nothing to check it. A semicolon or a colon does not cut — a segment may carry several claims, and which claim came from which clause is not something the span can honestly say.

A document cut into sentences: `[{:index i :text "…" :span [start end]} …]`.

The span is what everything downstream is for — `(subs doc start end)` is the segment's
text, exactly, so an accepted candidate points back at characters rather than at a
paraphrase.  A test holds that identity.

Cut at a run of `.`/`!`/`?` followed by whitespace or the end of the document, which is
crude and deliberately so: an abbreviation splits a segment in two, which costs a
narrower span and no correctness, while a cleverer splitter would need a model and put
one on the *reading* side of the pipeline where there is nothing to check it.  A
semicolon or a colon does **not** cut — a segment may carry several claims, and which
claim came from which clause is not something the span can honestly say.
raw docstring

spellingsclj

(spellings words)

The symbols a run of words could be a KB term spelled as.

The KB reads a term's role off its spelling, so a phrase has one candidate per convention: prepared for winter could be the predicate preparedForWinter or the type prepared_for_winter, and one word could be lion, Lion or its singular. Generating the spellings and asking the KB which exist is the whole of resolution — the alternative, inverting the KB's vocabulary into the words each term is written with, is the one read here that would grow with the knowledge base.

Nothing is filtered by plausibility: allThroughThe is generated and the KB says no, at the cost of one set lookup.

The symbols a run of words could be a KB term spelled as.

The KB reads a term's role off its spelling, so a phrase has one candidate per
convention: *prepared for winter* could be the predicate `preparedForWinter` or the type
`prepared_for_winter`, and one word could be `lion`, `Lion` or its singular.  Generating
the spellings and asking the KB which exist is the whole of resolution — the alternative,
inverting the KB's vocabulary into the words each term is written with, is the one read
here that would grow with the knowledge base.

Nothing is filtered by plausibility: `allThroughThe` is generated and the KB says no, at
the cost of one set lookup.
raw docstring

system-promptclj

The instruction half — static, because everything about the KB and the document rides in the user turn.

What it spends its tokens on is the four failures this direction has that the other three do not: inventing vocabulary the card already holds (the shared failure, and the one the card is there to prevent), stating a general claim as a fact with a loose variable, introducing a character twice under two names, and quietly translating what the text did not say. The last is the one no check downstream can catch, so it is the one stated most plainly: the reader would rather have a sentence reported as untranslatable than a fluent sentence that is not in the text.

The instruction half — static, because everything about the KB and the document rides in
the user turn.

What it spends its tokens on is the four failures this direction has that the other three
do not: inventing vocabulary the card already holds (the shared failure, and the one the
card is there to prevent), stating a general claim as a fact with a loose variable,
introducing a character twice under two names, and quietly translating what the text did
not say.  The last is the one no check downstream can catch, so it is the one stated
most plainly: the reader would rather have a sentence reported as untranslatable than a
fluent sentence that is not in the text.
raw docstring

user-turnclj

(user-turn kb segs resolved context instruction)
(user-turn kb segs resolved context instruction opts)

The volatile half: the numbered document, the vocabulary its own words resolved to, the context candidates are filed in, and the reader's instruction if they gave one.

The document comes first and the vocabulary after it, the opposite of the page path. The card is here to be consulted while translating rather than to be summarized, and a reader's instruction — when there is one — goes last so it is the newest thing in the window.

The volatile half: the numbered document, the vocabulary its own words resolved to, the
context candidates are filed in, and the reader's instruction if they gave one.

The document comes **first** and the vocabulary after it, the opposite of the page path.
The card is here to be consulted while translating rather than to be summarized, and a
reader's instruction — when there is one — goes last so it is the newest thing in the
window.
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