symfonic.capabilities.prompting.hms¶
hms ¶
The HMS system prompt, as a contribution the compiler owns (#21, path A).
Legacy renders this through HMSSystemPromptSection and then asks it whether
the result fits a budget; the engine reads that verdict and may ignore it. The
compiler, meanwhile, has its own budget that drops content. Keeping both after
the port would mean one prompt judged twice by different rules, one advisory and
one destructive — so the split here is deliberate and total:
- the source renders and validates. Placeholder substitution, the upper/lower key fallback, the unresolved-placeholder refusal and the tenant check are this class's contract, and they are the reason a port was chosen over a rewrite.
- the compiler decides what survives. No trimming, no truncation and no
budget verdict happens here. :func:
estimate_tokensis kept for characterization parity and is deliberately not consulted byread.
The trust tier is the other half, and it inverts the memory case. This text is
authored — a human wrote the template — so it is not untrusted input and must
not be delimited as such. A recalled memory is aggregated from what a user said
and defaults to untrusted=True; wrapping the system prompt the same way
would tell the model its own instructions are attacker-influenced.
HmsSystemSource
dataclass
¶
HmsSystemSource(state: dict[str, Any] = dict(), template_path: Path | None = None, untrusted: bool = False, offline_safe: bool = True, scope_aware: bool = True)
Renders the HMS system template for one turn's state.
state is held on the instance rather than read from a global, so two
agents in one process render two prompts. It carries the {{VARIABLE}}
values the template declares, including TENANT_ID.
Not frozen, and the reason is the protocol rather than a change of mind:
ContributionSource declares offline_safe and scope_aware as
settable, so a frozen dataclass cannot satisfy it. The isolation this class
needs comes from copying state at construction instead — a caller that
mutates the dict it passed in does not reach the rendered prompt, which is
the property that actually mattered.
estimate_tokens
staticmethod
¶
Legacy's conservative estimate, kept for characterization only.
len // 3 rather than len // 4 because the shorter divisor
under-counts structured text by 20-30%. Retained so a parity test can
compare the two paths on the same number, and not consulted by
:meth:read: the compiler owns the budget, and a second estimator
reachable from a source is how two gates reappear.
Source code in src/symfonic/capabilities/prompting/hms.py
read ¶
Render the template, or refuse.
Both refusals are the legacy ones and both are deliberate:
a missing tenant raises SecurityScopeError before any rendering,
because the tenant is what binds this prompt to the data it may discuss;
an unresolved placeholder raises ValueError rather than blanking,
because a blanked placeholder ships a grammatical, plausible system
prompt with an instruction missing.
The revision is the rendered text's own digest, so a template or state change moves the cache key. Nothing here consults a budget.
Source code in src/symfonic/capabilities/prompting/hms.py
hms_contribution ¶
Declare the HMS system prompt to the compiler, at an authored tier.
L0 and platform because the text is the deployment's own identity,
written by a human: the highest layer and the highest authority tier, which
is what places it ahead of anything learned and lets the render gate leave
it undelimited.
deployment scope for the same reason. The recall it will sit above is
tenant-scoped and re-checked per turn; this template is the same for every
tenant the process serves, and scoping it narrower would invite a per-tenant
system prompt that nothing here validates.
order is very negative so the system prompt leads its layer. It is a
default rather than a constant because ordering_key is (layer, order,
contribution_id) and an adopter with two L0 contributions needs a way to
say which comes first -- but the default must not tie, or the alphabetical
tie-break decides identity ordering by accident.