symfonic.kernel.pre_model¶
pre_model ¶
The pre-model seam: capability stages that read a round before it is sent.
The last silent rung. Phase.PRE_MODEL sat in PHASE_LADDER between
prompt-assembly and post-model while nothing dispatched it, so a
capability declaring a pre-model stage compiled into the plan, took its
place in the compiled order, received its effect grants, and was invoked in no
pass. That is the shape :mod:symfonic.kernel.post_tool records TA8.39 finding
for POST_TOOL; this is the same defect at the one phase it outlived, and it
is named in 11.0's Known gaps.
Why it is not a second prompt-assembly. That question decides whether
the phase is wired or deleted, so it is answered here rather than assumed.
prompt-assembly runs once per turn, before the round loop opens. This
rung runs once per round, inside it. It is therefore the only seam that sees
the transcript as it will be sent on this round -- including tool results
that a previous round's post-tool folded into it. A stage that must look at
the conversation the model is about to be given, rather than the one it was
given first, has nowhere else to stand.
Deliberately the same shape as :mod:symfonic.kernel.post_model and
:mod:symfonic.kernel.post_tool, and for their reasons rather than by
imitation:
- it does not bind. The turn is already open by the time a round begins;
RequestContext.bind_generationis write-once and raises on a second call. - it applies nothing.
apply=Noneis the documented case indispatch's docstring, not a placeholder. No contract exists for what apre-modelcontribution mutates, and inventing one here -- transcript amendment is the obvious candidate -- would be the fabricationT3.5.2refused onrun. A stage that must rewrite what the model reads haspre-tool's amendment contract as the worked precedent whenever someone brings the evidence for this phase's. - it grants explicitly.
grants=defaults toNoneinrun_phaseandNoneskips the effect check, so a new call site that forgets the argument reopens STG-8 in silence -- the PR #92 class, which is why a test names it.
PreModelContext
dataclass
¶
What a pre-model stage is handed for one round about to be sent.
run_pre_model
async
¶
run_pre_model(plan: Any, request: Any, *, resolved: Any = None, transcript: Any = None, handlers: Mapping[str, StageHandler] | None = None) -> tuple[StageTrace, ...]
Dispatch the pre-model stages for one round about to be sent.
Handlers come from plan.bindings.stage_handlers overlaid with
handlers, which exists for tests and for a composition root that has
not moved yet. Additive, so a plan's own handler cannot be silently
replaced.