@miadi/llmops
v0.4.0
Published
The LLMOps layer of the Miadi Factory: the records, instruments and assessments of language-model operations across three domains (Creative, Ceremonial, Engineering), behind a provider it never names. 0.1.0 holds the vocabulary as types, the address gramm
Readme
@miadi/llmops
The LLMOps layer of the Miadi Factory. It holds the records, instruments and assessments of language-model operations: the traces of what agents ran, the prompts they ran with, the datasets and metrics their work is tested against. It holds them across three domains, Creative, Ceremonial and Engineering, behind a provider it never names. It does not perform the operations (deployment, fine-tuning, serving).
Guillaume named it on 2026-10-19: "I did not like the word tracing for a package, because it involved like prompt management systems. It involves other parts of it." Tracing is one of its capabilities.
Its vocabulary is miadi-foundation:tracing-provider-vocabulary/vocabulary.md in jgwill/Miadi.
What 0.1.0 holds
- The vocabulary as types. Six axes, each with its own values and an English and French label:
domain (Creative, Ceremonial, Engineering), class (record, instrument, assessment), capability
(tracing, prompt management, dataset management, evaluation, usage accounting), circle (Story,
Stewardship, Engineering, Evaluation, Continuity), status (fact, hypothesis, envisioned, fiction,
from jgwill/Miadi#815) and source (captured, given, reported, retrieved, inferred, assumed). Each
resource an address names sits on class and capability: a
score-configis a metric, an instrument of evaluation. - The address grammar.
parseAddress,canonicalAddress,formatAddress. - A delayed step's session reference (jgwill/Miadi#824, Episode 297):
resolveSessionRefgives the session's trace and the pinned prompt version that rehydrates it. - The service over the first provider.
createLlmopsService()is@miadi/tracing's service over@miadi/tracing-langfuse. Each domain reads its own keys,<DOMAIN>_LANGFUSE_PUBLIC_KEY,_SECRET_KEYand_BASE_URL. It answers metadata only, never a key, a prompt body or a trace's input and output.
0.1.0 builds on @miadi/tracing and @miadi/tracing-langfuse and does not replace them.
Addresses
miadi-tracing:<domain>/<resource>/<id>[@<v>] canonical, every resource (trace, prompt, session, dataset, score-config)
miadi-trace:<domain>/<id> tracing → miadi-tracing:<domain>/trace/<id>
miadi-prompt:<domain>/<name>[@<v>] prompts → miadi-tracing:<domain>/prompt/<name>[@<v>]
miadi-score:<domain>/<id> evaluation → miadi-tracing:<domain>/score-config/<id>An address names the domain, the resource, the id and the version, and never the provider. A form
without a domain names nothing. Every miadi-tracing: address an episode already holds keeps
resolving: the canonical form is read by the same parseTracingRef the app and the chronicle use.
import { parseAddress, resolveSessionRef } from "@miadi/llmops"
parseAddress("miadi-score:eng/faithfulness").canonical
// "miadi-tracing:eng/score-config/faithfulness"
resolveSessionRef({ mission: "miadiRun-2610121041-pde-into-miadi-chronicle.v3" }, { domain: "ceremony" })
// { trace: null,
// rehydrate_with: "miadi-tracing:ceremony/prompt/miadiRun-2610121041-pde-into-miadi-chronicle@3",
// mission: "miadiRun-2610121041-pde-into-miadi-chronicle.v3" }A mission is <prompt-name>.v<N>, as the miadi-run skill spells it. A bare trace id or a mission
names no domain, so it resolves only when a domain is given. A prompt that is not pinned to a version
cannot rehydrate a session.
From a terminal:
npx @miadi/llmops address miadi-trace:ceremony/d87a4c119eb24fb4a718c29e130bd642
npx @miadi/llmops rehydrate --domain ceremony --mission miadiRun-x.v2Missions (0.2.0)
@miadi/llmops/missions holds three things (jgwill/Miadi#868):
- the record miadi-run keeps for a mission (
MissionRecord, schemamiadi-run/mission@1), withreadMissionRecords()over$MIADI_RUN_HOMEor~/.miadi/missions; - SwarmClaw's
Mission, the fields the view reads; MissionView, one read-only view of both.
The view uses SwarmClaw's field names wherever the meaning is the same (title, goal, successCriteria, rootSessionId, agentIds, status, milestones, createdAt, startedAt, endedAt), so a later sync writes to fields that already line up. It adds Miadi's own fields: sessions (how each session belongs: received, signed, fork-of, named-after, root, milestone), teams (from the sessions that did the work, never from one that only signed), episodes (numbers), and for miadi-run the pinned prompt and its trace.
import { fromMiadiRun, fromSwarmclaw, readMissionRecords } from "@miadi/llmops/missions"
readMissionRecords().map((r) => fromMiadiRun(r)) // status in SwarmClaw's words; sourceStatus keeps miadi-run's
fromSwarmclaw(mission) // episodes from the miadi-chronicle:// addresses in its goalfromMiadiRun() lists the seat and the sessions that signed the record. A host that reads the binding line passes forks and named sessions as sessions (@miadi/hooks-interpreter linkMissions). A miadi-run returned reads as completed, and sourceStatus keeps the original.
Since 0.3.1 the view has a third source, connector: a run_request or work_proposal the Miadi connector holds, which opens a session when a person approves it. fromConnector(input, { sessions }) takes the folded record's fields and the connector's projection (stage, label, from @miadi/connector/records projectRequest), so this package does not depend on the connector. Awaiting a decision reads as draft, sent back as paused, approved and dispatching as running, declined as cancelled. The seat its brief opened is received; a session its refs name is cited and lends no team. approvalUrl is the request's approval page.
Prompt labels (0.3.0)
A provider labels prompt versions (production, latest, any name a person gives). The factory gives
meaning to its own labels and leaves the provider's alone. The first is creative-ready: a Ceremonial
prompt version ready to steer the Author's Desk (D4, decided as E2 of the factory's next-step plan,
2026-10-26). A person sets it, or the seat sets it when its reading of the prompt finds it ready. A label
is reversible, and the prompt stays in its domain.
promptLabelsOf(ref) reads the labels of a prompt reference in any of its three shapes: labels (a
provider's PromptSummary, a ResourceSummary, a MissionView's prompt), labels_at_prepare (a
miadi-run record's prompt) and label (an episode's tracing entry). isCreativeReady(ref) and
factoryLabelsOf(ref) recognise the factory's labels. A miadi-run mission view's prompt.labels now
carries the labels the version had when it was pinned.
import { isCreativeReady, promptLabelsOf } from "@miadi/llmops"
promptLabelsOf({ labels: ["production", "creative-ready"] }) // ["production", "creative-ready"]
isCreativeReady({ label: "creative-ready" }) // true, an episode's tracing entryAccess to the tracing domains on a host (0.4.0)
Each domain is one Langfuse project, reached with three keys:
<DOMAIN>_LANGFUSE_PUBLIC_KEY, _SECRET_KEY and _BASE_URL, for CEREMONY, CREATIVE and ENG.
The keys are secrets of the person or seat that uses them, so they live at user level, one file per
user (decided 2026-11-12, jgwill/Miadi#824):
~/.config/miadi/tracing.env mode 0600, beside the other per-user files in ~/.config/miadi/They are never kept in /etc/miadi/miadi.env. That file comes from the miadi-config deb, every
user can read it, and its own header rules out secrets. A value is taken from the first place that has it:
- the environment
- the file named by
$MIADI_TRACING_ENV ~/.config/miadi/tracing.env$MIADI_SRC/.env, where the keys lived before 2026-11-12, so a host that has not moved still reads
Files are read as literal KEY=VALUE lines, never sourced. @miadi/llmops/keys (Node only) holds the
reading: loadTracingEnv() fills the environment for createLlmopsService({ env }) or a child
process, domainKeyStates() says where each name came from without its value, and
langfuseEnvFor(domain) gives a domain's keys under the names the provider's SDK and coaia read.
miadi-run and scripts/tracing-domain.sh read the same file in the same order.
On a new host, for each user that traces:
npx @miadi/llmops domains --init # writes ~/.config/miadi/tracing.env with the nine names, mode 0600, never overwrites
$EDITOR ~/.config/miadi/tracing.env # one key pair per project: Project Settings > API Keys
npx @miadi/llmops domains --check # one JSON line per domain: where each key came from, and the project it reachesdomains exits 1 while a domain lacks a key or, with --check, does not reach its project, and warns
when the file can be read by others. To run any Langfuse client as one domain:
npx @miadi/llmops env eng -- coaia fuse prompts listA project key reads and writes its project's traces, prompts, datasets, scores and evaluation rules.
It cannot read the organization's export settings (/api/public/integrations/blob-storage asks for an
organization-scoped key), and no file here holds one.
Left for later
- Ceremonial prompts around the Author's Desk, Creative prompts from its surface (Q4, answered
by Guillaume on 2026-10-20). No prompt is moved; since 0.3.0 a Ceremonial prompt the desk may use
carries
creative-ready. The story, writing and creative prompts of the Ceremonial domain surround the desk. The author writes fiction at its center, and that writing makes prompts in the Creative domain, each able to name the Ceremonial prompts it was written with by their addresses. The desk is built in Episode 560; jgwill/Miadi#769 holds the design and what is open. - The wheel direction of each circle and artefact kind (Q1 of the vocabulary).
- The wheel seat for judgments, jgwill/medicine-wheel#161.
- Moving the app's doors from
@miadi/tracingto@miadi/llmops. Until then the :3335 open and resolve doors read addresses through@miadi/inquiry-weave, which does not readmiadi-score:. - Short forms for sessions and datasets. Not chosen; they are addressed in the canonical form.
Ref: jgwill/Miadi#824, jgwill/Miadi#769.
