The model's read surface over a KB — generated, not hand-written.
vaelii.impl.serve/ops is already an allowlisted, EDN-typed map of vaelii.core
calls: the exact surface the browser and the daemon reach a KB through. So the
tool schemas are derived from its read subset rather than transcribed, and the
tool calls are dispatched back through the same table. A read added to serve/ops
becomes a tool with no edit here; a read renamed there cannot rot a copy here,
because there is no copy.
The model never writes. write-ops names every mutating op, and anything
resolving to a ! var is treated as one whatever the table says, so the exposed set
is reads only. The model's output is a proposed batch, reviewed and applied by a
human (vaelii.impl.llm.session) — there is no write tool and no write path.
Argument shapes come from the vaelii.core var's own :arglists (minus the leading
kb) and its docstring, so a signature change is picked up on the next build. JSON
carries no symbols, so a sentence / context / term argument is a string holding an
EDN s-expression — "(dog ?x)", "WellContext" — read back with
clojure.edn/read-string (never read-string: EDN has no reader-eval, so a model's
output cannot evaluate code).
The model's read surface over a KB — **generated**, not hand-written. `vaelii.impl.serve/ops` is already an allowlisted, EDN-typed map of `vaelii.core` calls: the exact surface the browser and the daemon reach a KB through. So the tool schemas are derived from its **read subset** rather than transcribed, and the tool calls are dispatched back through the same table. A read added to `serve/ops` becomes a tool with no edit here; a read renamed there cannot rot a copy here, because there is no copy. **The model never writes.** `write-ops` names every mutating op, and anything resolving to a `!` var is treated as one whatever the table says, so the exposed set is reads only. The model's *output* is a proposed batch, reviewed and applied by a human (`vaelii.impl.llm.session`) — there is no write tool and no write path. Argument shapes come from the `vaelii.core` var's own `:arglists` (minus the leading `kb`) and its docstring, so a signature change is picked up on the next build. JSON carries no symbols, so a sentence / context / term argument is a **string holding an EDN s-expression** — `"(dog ?x)"`, `"WellContext"` — read back with `clojure.edn/read-string` (never `read-string`: EDN has no reader-eval, so a model's output cannot evaluate code).
(call kb tname input)(call kb tname input {:keys [max-result-chars] :or {max-result-chars 4000}})Run one tool call against kb and return {:ok true :result "…"} or
{:ok false :error "…"} — the string a tool_result block carries back.
input is the provider's JSON object (string keys). The longest signature the
input fully satisfies decides the call, so an op with several shapes (why-not
takes a handle or a sentence and a context) dispatches to the one the model
actually supplied. Dispatch goes through serve/ops, the same table the schemas
were generated from.
Never throws: a bad argument, an unknown tool, or a refusal from the KB comes back
as {:ok false :error "…"}, which is what a tool_result block wants — the model
reads the error and tries again.
opts: :max-result-chars (default 4000) bounds what a broad read can push into
the context window.
Run one tool call against `kb` and return `{:ok true :result "…"}` or
`{:ok false :error "…"}` — the string a `tool_result` block carries back.
`input` is the provider's JSON object (string keys). The **longest signature the
input fully satisfies** decides the call, so an op with several shapes (`why-not`
takes a handle *or* a sentence and a context) dispatches to the one the model
actually supplied. Dispatch goes through `serve/ops`, the same table the schemas
were generated from.
Never throws: a bad argument, an unknown tool, or a refusal from the KB comes back
as `{:ok false :error "…"}`, which is what a `tool_result` block wants — the model
reads the error and tries again.
`opts`: `:max-result-chars` (default 4000) bounds what a broad read can push into
the context window.(op-of tname)The op keyword a tool name came from, or nil if it names no exposed read.
The op keyword a tool name came from, or nil if it names no exposed read.
(read-ops)The op keywords the model may call: serve/ops minus the writes, minus anything
that does not resolve to a non-variadic vaelii.core var. Sorted, so the generated
tool list is byte-stable and the prompt cache survives a rebuild.
The op keywords the model may call: `serve/ops` minus the writes, minus anything that does not resolve to a non-variadic `vaelii.core` var. Sorted, so the generated tool list is byte-stable and the prompt cache survives a rebuild.
(schema op)The tool schema for one op keyword.
The tool schema for one op keyword.
(schemas)(schemas {:keys [only exclude]})Every exposed read as a tool schema, in read-ops order. opts:
:only a set of op keywords to keep (default: all reads) :exclude a set of op keywords to drop
Every exposed read as a tool schema, in `read-ops` order. `opts`: :only a set of op keywords to keep (default: all reads) :exclude a set of op keywords to drop
(tool-name op)The tool name for an op keyword — :find-sentexes -> kb_find_sentexes,
:ask? -> kb_ask_p.
The tool name for an op keyword — `:find-sentexes` -> `kb_find_sentexes`, `:ask?` -> `kb_ask_p`.
The mutating ops in serve/ops. A new mutating op must be listed here — the
exposed tool set is (keys serve/ops) minus this, so an omission would hand the
model a write. Ops resolving to a ! var are excluded independently, which catches
the ones spelled with the warning already.
That second mechanism is a backstop, not the rule: ! means irreversible
(docs/api.md), and a write that merely mutates is spelled without one. So every
mutating op is listed here explicitly, including the ones the ! sweep cannot see —
:edit and :edit-with-consequences both store, and neither carries the suffix.
The mutating ops in `serve/ops`. A new mutating op **must** be listed here — the exposed tool set is `(keys serve/ops)` minus this, so an omission would hand the model a write. Ops resolving to a `!` var are excluded independently, which catches the ones spelled with the warning already. That second mechanism is a backstop, not the rule: `!` means *irreversible* (`docs/api.md`), and a write that merely mutates is spelled without one. So every mutating op is listed here explicitly, including the ones the `!` sweep cannot see — `:edit` and `:edit-with-consequences` both store, and neither carries the suffix.
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 |