2026-07-17 03:06:23 +00:00
|
|
|
|
# LLM Runtime Internals
|
|
|
|
|
|
|
2026-07-26 13:33:45 +00:00
|
|
|
|
`internal/framework/llm` is Notarius’s provider-independent structured
|
2026-07-28 16:31:36 +00:00
|
|
|
|
completion boundary. It adapts framework requests to PromptKit, bounds
|
2026-07-26 13:33:45 +00:00
|
|
|
|
provider calls, assembles registered prompt and schema assets, records selected
|
|
|
|
|
|
profiles, and redacts provider errors. The architectural boundary is defined in
|
|
|
|
|
|
[Architecture](../policy/architecture.md#llm-boundary); profile sources,
|
|
|
|
|
|
credentials, and concurrency settings belong in
|
2026-07-28 16:52:49 +00:00
|
|
|
|
[Configuration](../config.md#promptkit-profiles) and
|
2026-07-26 13:33:45 +00:00
|
|
|
|
[Configuration](../config.md#concurrency-output-cache-and-debug).
|
|
|
|
|
|
|
|
|
|
|
|
## Structured Completion Boundary
|
|
|
|
|
|
|
|
|
|
|
|
Modules and LLM-backed validators depend only on
|
|
|
|
|
|
`contracts.StructuredLLMClient`. A completion request supplies a prompt ID and
|
|
|
|
|
|
version, optional profile and session IDs, named input material, variables, and
|
|
|
|
|
|
a caller-owned decode target. The successful response returns the validated raw
|
|
|
|
|
|
structured bytes together with non-secret provider, model, profile, and token
|
|
|
|
|
|
metadata.
|
|
|
|
|
|
|
|
|
|
|
|
The caller owns the domain behavior: it chooses the prompt, prepares inputs,
|
|
|
|
|
|
selects the private response schema, and interprets the decoded result. The
|
|
|
|
|
|
adapter does not own source evidence, artifact conversion, normalization, or
|
|
|
|
|
|
durable schemas. Those responsibilities remain with the module and its
|
|
|
|
|
|
[integration contract](../integrations/).
|
|
|
|
|
|
|
2026-07-28 16:31:36 +00:00
|
|
|
|
`PromptKitClient` validates the request target and prompt identity, maps each
|
|
|
|
|
|
named material to a PromptKit inline artifact while preserving its origin URI,
|
2026-07-30 01:58:40 +00:00
|
|
|
|
maps the trimmed request session to PromptKit's direct per-run session field,
|
|
|
|
|
|
retains the same value as the `session_id` prompt variable for maintained
|
2026-08-03 16:17:40 +00:00
|
|
|
|
prompt compatibility, and forwards profile selection. It then creates one
|
|
|
|
|
|
frozen prepared execution, captures its caller-owned credential-redacted
|
2026-08-03 17:21:42 +00:00
|
|
|
|
details for debug material, and executes that exact snapshot through
|
|
|
|
|
|
PromptKit's prepared-execution boundary. The direct field
|
2026-08-03 16:17:40 +00:00
|
|
|
|
is authoritative for provider session behavior. A session ID is a stable,
|
|
|
|
|
|
non-secret correlation identifier and may be exposed to providers and provider
|
|
|
|
|
|
observability. The adapter returns PromptKit’s validated raw bytes rather than
|
|
|
|
|
|
re-encoding the decoded target. An empty optional material is represented as
|
|
|
|
|
|
one space so its named input is retained by PromptKit.
|
2026-07-26 13:33:45 +00:00
|
|
|
|
|
2026-07-30 02:03:04 +00:00
|
|
|
|
Client construction may also receive a run-wide reasoning-effort override from
|
|
|
|
|
|
the CLI factory boundary. The adapter copies the caller-owned pointer and
|
|
|
|
|
|
creates a fresh PromptKit execution override for each request: a nil pointer
|
|
|
|
|
|
inherits the selected profile, a non-empty value replaces it, and an empty
|
2026-07-30 02:11:35 +00:00
|
|
|
|
value clears inherited reasoning. The CLI's mutually exclusive
|
|
|
|
|
|
`--reasoning-effort` and `--clear-reasoning-effort` controls select those
|
|
|
|
|
|
states. With neither flag, profile behavior remains unchanged. Because
|
|
|
|
|
|
production constructs one shared client, the selected state applies uniformly
|
|
|
|
|
|
to module calls, retries, and LLM-backed validators for the whole run.
|
2026-07-30 02:03:04 +00:00
|
|
|
|
|
2026-08-03 16:26:15 +00:00
|
|
|
|
An empty request profile lets the prompt select its configured default. Before a
|
2026-08-03 17:21:42 +00:00
|
|
|
|
run begins, the CLI asks the adapter to inspect every explicit profile on the
|
|
|
|
|
|
resolved selected LLM-backed bindings and validators, including inherited
|
|
|
|
|
|
pipeline profiles. Inspection resolves the profile and its selected backend and
|
2026-08-03 16:26:15 +00:00
|
|
|
|
target without loading a prompt, reading credentials, admitting capacity, or
|
|
|
|
|
|
contacting a provider, so a missing or invalid explicit profile fails before
|
|
|
|
|
|
stage execution while a valid `api_key_env` may remain unset. Calls record the
|
|
|
|
|
|
profile actually selected by PromptKit. The recorder trims and deduplicates
|
|
|
|
|
|
non-secret profile identity, provider, model, selected backend ID, and
|
|
|
|
|
|
effective reasoning values for manifest use. Entries that differ in backend or
|
|
|
|
|
|
reasoning remain distinct and deterministically ordered. Endpoint-only profiles
|
|
|
|
|
|
retain an empty backend ID, which the published JSON omits. Successful
|
|
|
|
|
|
completion responses and recorded profile manifests identify the adapter
|
|
|
|
|
|
provider as `promptkit`.
|
2026-07-26 13:33:45 +00:00
|
|
|
|
|
2026-08-03 16:26:15 +00:00
|
|
|
|
The CLI's profile-inspection engine and the production adapter use the same
|
2026-08-03 16:34:48 +00:00
|
|
|
|
profile-source construction to apply the configured profile directory or file,
|
|
|
|
|
|
the optional registered fallback profile assets, and the optional conventional
|
|
|
|
|
|
`local` backend. Preflight therefore resolves the same profile sources and
|
|
|
|
|
|
backend membership as runtime without performing generation. Fallback assets
|
2026-08-03 16:39:45 +00:00
|
|
|
|
are mounted only when at least one source is registered. The production D&D
|
|
|
|
|
|
registrar contributes its `dnd-extraction` fallback, and the maintained D&D
|
|
|
|
|
|
prompts select that logical ID by default. PromptKit owns source precedence and
|
|
|
|
|
|
profile parsing: an operator-provided matching profile takes precedence over a
|
|
|
|
|
|
fallback profile without Notarius merging either document.
|
2026-08-03 16:34:48 +00:00
|
|
|
|
When the registration is absent, a profile selecting `backend: local` fails
|
|
|
|
|
|
inspection instead of falling back to a built-in or endpoint-only target.
|
2026-07-30 05:22:41 +00:00
|
|
|
|
|
2026-07-28 13:36:01 -05:00
|
|
|
|
Before execution, the adapter also contributes a non-secret checkpoint
|
|
|
|
|
|
fingerprint for the effective PromptKit profile source. It combines the
|
|
|
|
|
|
identity of PromptKit's compiled-in profile catalog with a deterministic digest
|
|
|
|
|
|
of every YAML profile in the configured profile directory, or of the configured
|
2026-08-03 16:34:48 +00:00
|
|
|
|
profile file, and a deterministic digest of the flattened fallback profile
|
|
|
|
|
|
assets. The fingerprint contains neither profile content nor source paths. It
|
2026-08-03 17:21:42 +00:00
|
|
|
|
covers inherited pipeline profiles, explicit binding profiles, and
|
|
|
|
|
|
prompt-selected defaults, so changing a model or other profile setting cannot
|
|
|
|
|
|
reuse checkpoints created under the
|
2026-08-03 16:34:48 +00:00
|
|
|
|
prior profile source. This cache identity is independent of durable
|
2026-07-28 13:36:01 -05:00
|
|
|
|
profile provenance: run manifests continue to list only profiles actually
|
2026-07-30 05:22:41 +00:00
|
|
|
|
observed during LLM calls. When the local backend is registered, a second
|
|
|
|
|
|
fingerprint hashes its trimmed endpoint behind a stable marker. Changing that
|
|
|
|
|
|
semantic execution target invalidates checkpoint reuse. The raw endpoint is not
|
|
|
|
|
|
stored in checkpoint identity, and the local concurrency limit is excluded
|
|
|
|
|
|
because it changes scheduling rather than execution semantics.
|
2026-07-28 13:36:01 -05:00
|
|
|
|
|
2026-07-26 13:33:45 +00:00
|
|
|
|
## Shared Provider-Call Limit
|
|
|
|
|
|
|
2026-07-28 16:31:36 +00:00
|
|
|
|
Production construction creates one PromptKit client and wraps it in one
|
2026-07-26 13:33:45 +00:00
|
|
|
|
scheduled client. The scheduler has a fixed, positive permit limit, serves
|
|
|
|
|
|
queued calls in FIFO order, and removes a queued call when its context is
|
|
|
|
|
|
cancelled. A granted permit is released exactly once on every completion path.
|
|
|
|
|
|
|
|
|
|
|
|
The scheduled wrapper surrounds every `CompleteStructured` call, so concurrent
|
|
|
|
|
|
lanes, pipeline retries, and LLM-backed validators share the same provider-call
|
|
|
|
|
|
ceiling. This ceiling is independent of pipeline worker concurrency; changing
|
|
|
|
|
|
worker counts cannot exceed the configured LLM limit. The configuration field
|
|
|
|
|
|
and its effective default are owned by
|
|
|
|
|
|
[Configuration](../config.md#concurrency-output-cache-and-debug).
|
2026-07-17 08:49:08 +00:00
|
|
|
|
|
2026-07-30 02:23:51 +00:00
|
|
|
|
PromptKit applies a second, independent admission limit when the selected
|
|
|
|
|
|
profile names a limited backend. It sits beneath the Notarius scheduled client,
|
|
|
|
|
|
so it may narrow but cannot expand the application-wide limit. Built-in
|
|
|
|
|
|
OpenRouter profiles select PromptKit's reserved backend and its upstream
|
2026-07-30 05:22:41 +00:00
|
|
|
|
capacity policy. A positive configured local-backend limit bounds active local
|
|
|
|
|
|
generations inside PromptKit; zero leaves that backend unlimited there.
|
|
|
|
|
|
Endpoint-only profiles do not select a PromptKit backend and remain limited
|
|
|
|
|
|
only by the Notarius scheduler.
|
2026-07-30 02:23:51 +00:00
|
|
|
|
|
2026-07-17 03:06:23 +00:00
|
|
|
|
## Prompt And Schema Assets
|
|
|
|
|
|
|
2026-08-03 16:34:48 +00:00
|
|
|
|
An `AssetRegistry` collects prompt, schema, and optional fallback-profile
|
|
|
|
|
|
filesystems from production module families. It flattens registered roots into
|
|
|
|
|
|
the corresponding PromptKit filesystems and rejects invalid roots, unreadable
|
|
|
|
|
|
assets, duplicate paths, and missing prompt or schema files during preparation.
|
|
|
|
|
|
Fallback assets receive a safe content digest for checkpoint identity; raw
|
|
|
|
|
|
paths and bytes are never included. The framework’s `promptfs` helper combines
|
2026-07-26 13:33:45 +00:00
|
|
|
|
module-owned prompt files with reusable domain fragments without making the
|
|
|
|
|
|
framework depend on D&D content.
|
|
|
|
|
|
|
|
|
|
|
|
Each LLM-backed module owns its prompt declaration, package-specific assets,
|
|
|
|
|
|
and private response schema. Shared D&D wording is owned by the D&D shared
|
|
|
|
|
|
asset package; the detailed D&D conventions are in
|
|
|
|
|
|
[D&D Module Internals](dnd.md). The mounted prompt assets used by a module also
|
|
|
|
|
|
determine its prompt fingerprint. Schema loaders validate JSON, attach identity
|
|
|
|
|
|
and digest metadata, make defensive copies, and expose diagnostics without raw
|
|
|
|
|
|
schema bytes.
|
|
|
|
|
|
|
|
|
|
|
|
Private response schemas validate a model transport envelope. They are not the
|
|
|
|
|
|
durable artifact schema and should not be documented as an external wire
|
|
|
|
|
|
contract. Durable formats and compatibility rules remain in the
|
|
|
|
|
|
[integration contracts](../integrations/).
|
|
|
|
|
|
|
|
|
|
|
|
## Prompt Maintenance And Backend Caching
|
|
|
|
|
|
|
|
|
|
|
|
Prompt message order and shared asset bytes are runtime behavior. Backend cache
|
2026-07-27 20:16:11 +00:00
|
|
|
|
reuse depends on identical preceding roles, rendered bytes, and cache-control
|
|
|
|
|
|
metadata—not merely equivalent meaning. Keep reusable shared assets
|
|
|
|
|
|
byte-identical and preserve each prompt’s declared ordering and cache controls
|
|
|
|
|
|
when editing it.
|
|
|
|
|
|
|
|
|
|
|
|
For sibling prompts that can reuse the same source material, order universal
|
|
|
|
|
|
shared context first, request source material next, and module-specific
|
|
|
|
|
|
suffixes last. Put a cache boundary at a reusable prefix that is useful to the
|
|
|
|
|
|
backend. Redundant intermediate cache boundaries do not extend that reusable
|
|
|
|
|
|
prefix and add no value.
|
|
|
|
|
|
|
|
|
|
|
|
Prompt-family owners may choose a different sequence when their inputs and
|
|
|
|
|
|
reuse pattern differ. The D&D family’s extraction, scene-chunking, and NPC
|
|
|
|
|
|
normalization policies are maintained in [D&D Module Internals](dnd.md#prompt-construction).
|
|
|
|
|
|
Do not add tests that enforce prompt prose; prompt tests should verify the
|
|
|
|
|
|
meaningful input placement and cache controls of the prompt being changed.
|
2026-07-26 13:33:45 +00:00
|
|
|
|
|
|
|
|
|
|
## Validation, Repair, And Retries
|
|
|
|
|
|
|
2026-07-28 16:31:36 +00:00
|
|
|
|
PromptKit performs prompt rendering, provider execution, and the prompt’s
|
2026-07-26 13:33:45 +00:00
|
|
|
|
structured-output validation. The adapter reports an empty result, validation
|
|
|
|
|
|
failure, empty structured body, or decode failure as
|
|
|
|
|
|
`ErrInvalidStructuredOutput`, while retaining the returned raw bytes and debug
|
|
|
|
|
|
material when they exist. Provider failures remain operational errors rather
|
|
|
|
|
|
than output-validation failures.
|
|
|
|
|
|
|
2026-07-30 02:23:51 +00:00
|
|
|
|
When PromptKit rejects backend admission before generation, the adapter maps
|
|
|
|
|
|
`promptkit.ErrCapacityExceeded` to
|
|
|
|
|
|
`contracts.ErrLLMCapacityExceeded`, retaining prompt context and a redacted
|
2026-08-03 16:17:40 +00:00
|
|
|
|
upstream diagnostic without exposing the PromptKit sentinel or capacity-error
|
|
|
|
|
|
type as a framework contract. When supplied, the normalized selected backend
|
|
|
|
|
|
ID appears only in that safe application-owned diagnostic context. A canceled
|
|
|
|
|
|
caller context takes precedence. The adapter does not retry capacity failures;
|
|
|
|
|
|
the pipeline's existing binding attempt policy sees the operational error and
|
|
|
|
|
|
decides whether to rerun the complete operation.
|
2026-07-30 02:23:51 +00:00
|
|
|
|
|
2026-07-28 16:31:36 +00:00
|
|
|
|
Prompt-declared repair is executed within PromptKit’s structured-output flow.
|
2026-07-26 13:33:45 +00:00
|
|
|
|
The current production D&D prompt manifests set repair attempts to zero. That
|
|
|
|
|
|
setting does not replace pipeline retry behavior: a binding’s configured retry
|
|
|
|
|
|
count reruns its stage attempt after an error or rejection, and an exhausted
|
|
|
|
|
|
rejection is a recorded output rather than a provider error. The pipeline owns
|
|
|
|
|
|
attempt lifecycle, validation chains, and retry diagnostics; see
|
|
|
|
|
|
[Pipeline Internals](pipeline.md#validation-retries-and-output) and the
|
|
|
|
|
|
[binding reference](../config.md#module-bindings-and-validators).
|
|
|
|
|
|
|
2026-07-28 16:52:49 +00:00
|
|
|
|
## Timeout Ownership
|
|
|
|
|
|
|
|
|
|
|
|
The caller context remains the outer cancellation authority. PromptKit applies
|
|
|
|
|
|
a positive effective generation timeout as an inner request deadline; an
|
|
|
|
|
|
explicit zero disables only that generation deadline. The HTTP client timeout
|
|
|
|
|
|
is a separate transport-wide cap. Notarius forwards the caller context and
|
|
|
|
|
|
does not install another timeout wrapper around PromptKit.
|
|
|
|
|
|
|
|
|
|
|
|
The selected PromptKit profile owns generation settings. Notarius binding
|
|
|
|
|
|
retries remain outside the adapter and repeat the complete module operation
|
2026-07-30 01:58:40 +00:00
|
|
|
|
and validation chain. PromptKit does not add a provider retry loop.
|
2026-07-28 16:52:49 +00:00
|
|
|
|
Operator-facing behavior is summarized in
|
|
|
|
|
|
[Operations](../operations.md#operational-limits), and the pinned upstream
|
|
|
|
|
|
contract is identified in
|
|
|
|
|
|
[PromptKit Integration](../integrations/pkg-promptkit.md).
|
|
|
|
|
|
|
2026-07-26 13:33:45 +00:00
|
|
|
|
## Observability And Redaction
|
|
|
|
|
|
|
|
|
|
|
|
When debug recording is enabled, the pipeline decorates the shared client. The
|
|
|
|
|
|
wrapper records prepared prompt and response material, timing, selected profile
|
2026-07-30 02:18:14 +00:00
|
|
|
|
and backend, effective model parameters, and call identifiers in the run’s
|
|
|
|
|
|
debug bundle, including material available from a failed structured completion.
|
|
|
|
|
|
Effective parameters use PromptKit's stable lower-case JSON field names and may
|
|
|
|
|
|
include `backend_id`. For a successful completion, a debug-write failure is
|
|
|
|
|
|
surfaced; when the completion already failed, its call error remains the
|
|
|
|
|
|
result. Debug-bundle location, retention, and handling are operational concerns
|
|
|
|
|
|
documented in [Operations](../operations.md#debug-bundles).
|
|
|
|
|
|
|
|
|
|
|
|
Run manifests receive selected profile summaries, including optional effective
|
|
|
|
|
|
backend and reasoning provenance, and component identities—not prompt, schema,
|
|
|
|
|
|
source, reference, or response content. The published field semantics belong
|
|
|
|
|
|
to the [JSON output contract](../integrations/json-output.md#manifestjson).
|
|
|
|
|
|
Provider error text is wrapped with prompt context and bearer credentials are
|
|
|
|
|
|
redacted before it crosses the runtime boundary. Known-secret redaction is
|
|
|
|
|
|
available to other runtime collaborators; it does not make prompt or response
|
|
|
|
|
|
contents safe for general logging.
|
2026-07-26 13:33:45 +00:00
|
|
|
|
|
|
|
|
|
|
## Failure Boundaries
|
|
|
|
|
|
|
|
|
|
|
|
- Construction fails for missing asset registries, mutually exclusive profile
|
|
|
|
|
|
sources, invalid asset registration, or a non-positive scheduler limit.
|
|
|
|
|
|
- Preparation failures, unavailable explicit profiles, provider failures, and
|
|
|
|
|
|
context cancellation propagate to the calling stage with context.
|
2026-07-30 02:23:51 +00:00
|
|
|
|
- Backend admission exhaustion is a provider-neutral operational error and is
|
|
|
|
|
|
not classified as invalid structured output or validator rejection.
|
2026-07-26 13:33:45 +00:00
|
|
|
|
- Malformed or schema-invalid provider output is classified separately as
|
|
|
|
|
|
invalid structured output so the module or pipeline can apply its own retry
|
|
|
|
|
|
and rejection policy.
|
|
|
|
|
|
- Domain semantic checks, evidence decisions, and deterministic normalization
|
|
|
|
|
|
run outside the provider adapter.
|
|
|
|
|
|
|
|
|
|
|
|
## Focused Verification
|
|
|
|
|
|
|
|
|
|
|
|
Read the LLM adapter, scheduler, asset registry, schema loader, and redaction
|
|
|
|
|
|
tests when changing this boundary. Prompt changes also require the owning
|
|
|
|
|
|
module’s asset tests, and retry or debug changes require focused pipeline or
|
|
|
|
|
|
CLI coverage. The focused runtime and D&D checks are:
|
|
|
|
|
|
|
|
|
|
|
|
~~~sh
|
|
|
|
|
|
go test ./internal/framework/llm/... ./internal/modules/dnd/...
|
|
|
|
|
|
~~~
|