symfonic.capabilities.prompting.gates¶
gates ¶
Render gates: what must hold before a contribution's bytes reach the prompt.
Authored content renders verbatim. Learned content renders as data, inside a delimiter wrapper the content itself may not forge, because a profile assembled from what a user typed is attacker-influenced input sitting in the same window as the operator's instructions.
Three neutralisations do the load-bearing work inside the wrapper:
- Delimiter lookalikes — anything shaped like the opening or closing tag,
matched loosely (any separator between
untrustedanddata, optional closing bracket, case-insensitive) because</untrusted:data>and ``
GateResult
dataclass
¶
What a gate decided for one contribution.
RenderPolicy
dataclass
¶
RenderPolicy(max_learned_chars: int = 500, render_when: Callable[[PromptContribution], Any] | None = None)
The limits applied to learned content, and the set-wide render gate.
Authored content is deliberately uncapped: it is operator configuration, already reviewed by whoever deployed it, and a cap there would silently delete instructions the operator can see in their own config file.
render_when is S01's counterpart to the legacy per-block predicate, and
it lives here rather than on the contribution for the reason
PROMPT_BLOCK_CONTRACT published ahead of the port: the kernel gate is a
policy over the whole set, so a host predicate migrates as a policy rule.
It is consulted before any source is read, so a gated-off contribution
costs no I/O -- the same ordering PromptBlockResolver.resolve_block
documents. It may return an awaitable, in which case only the asynchronous
door can honour it; the synchronous one refuses by name.
neutralise_delimiters ¶
Replace every delimiter-shaped sequence in value.
Two rules, because "looks like the closing delimiter" is a question with a lexical answer and a visual one, and content that defeats either has escaped the wrapper.
Source code in src/symfonic/capabilities/prompting/gates.py
normalise_learned ¶
Strip control characters, then neutralise delimiter forgeries.
render_contribution ¶
render_contribution(contribution: PromptContribution, read: SourceRead, policy: RenderPolicy = DEFAULT_POLICY) -> GateResult
Gate one read and return the text that may render, or None.
Raises only on a trust/tier mismatch: that is the one failure where continuing means putting attacker-influenced text where the model reads operator instruction. Every other refusal drops with a diagnostic.
Source code in src/symfonic/capabilities/prompting/gates.py
sanitise_name ¶
select_body ¶
The bytes a contribution offers, after profile_fields selects (S01).
None means the contribution declared fields and the source named none of
them. That is a drop rather than a fallback to the prose: falling back
would render everything the source knows about a person on the turn an
operator narrowed the selection to one field, which is the opposite of what
they asked for.
Values render sorted by key so two compiles of one request produce one
prompt -- the same totality reason ordering_key gives.