symfonic.kernel.prompt_assembly¶
prompt_assembly ¶
Running the prompt-assembly phase — the first phase that dispatches.
Split from :mod:symfonic.kernel.invoker because it is a different kind of
thing. The invoker holds the kernel's phase operations — bind, assemble,
finish — each a small operation on a plan. This module is the composition of
one of those with the dispatcher: which handlers are in the table, how a
contribution is applied, what the caller gets back. Keeping them together took
invoker.py over its 300-line budget, and the budget was right: the two are
read for different reasons.
Prompt-assembly went first for a structural reason, not an arbitrary one. Memory
hands its hydration to the prompt compiler
(capabilities/memory/contribution.py), so any capability that wants to reach
a turn's prompt needs this phase to dispatch before it can. It is the phase the
rest of the migration is gated on.
StageContext
dataclass
¶
StageContext(plan: Any, request: Any, stage: Any, assembly: Any = None, resolved: ResolvedInputs = ResolvedInputs())
What a prompt-assembly stage is handed.
Deliberately no RequestContext: decision 2 says a stage returns rather
than mutates, so handing it the mutable run context would be handing it
precisely the thing it must not touch. A stage that needs to record
something returns it; the dispatcher decides what happens next.
assembly is the exception that makes composition possible, and it is
read-only in the sense that matters: the stage cannot write to it, only
return a successor. Without it a second prompt-assembly stage could not
see what the first produced, so the only assembly it could return was one
built from scratch -- and the dispatcher would adopt it, discarding the
first stage's work with no trace. That is what invoker.assemble_prompt
meant by "something to receive and something to hand back"; dispatch landed
with only the handing back.
kernel_prompt_handler ¶
The handler for the compiler's synthesized kernel.prompt stage.
W2/1b decision 4. The compiler synthesizes that stage and gave it no handler, so a rule of "every stage resolves a handler" would have failed every compilation — and the obvious escape, exempting kernel-owned stages, creates a class of stage whose inertness no rule covers. The distinction that holds is executable stage vs structural marker: if it runs, it has a handler, no matter who declared it.
No recursive dispatch. assemble stays the pure function it was; this is
the thin adapter that brings it to the stage protocol, so correct logic is
reused rather than rewritten one layer up.
Source code in src/symfonic/kernel/prompt_assembly.py
run_prompt_assembly
async
¶
run_prompt_assembly(kernel: Any, plan: Any, ctx: Any, request: TurnRequest, *, handlers: Mapping[str, StageHandler] | None = None) -> tuple[Any, tuple[StageTrace, ...]]
bind, then run prompt-assembly through the dispatcher.
This is what makes W2/1b not-inert: the stage program the compiler has
always produced is finally executed. The kernel's own kernel.prompt
stage is registered here rather than special-cased — it resolves a handler
like anyone else.
Handlers come from plan.bindings.stage_handlers — G6, where the plan
already keeps its live callables. The handlers argument overlays them and
exists for tests and for a composition root that has not yet moved: it is
additive, never a replacement, so a plan's own handlers cannot be silently
swapped out by a caller.
Returns the opened turn and the traces, so a caller can see what ran, what changed nothing and why — the record that makes an inert stage visible at runtime the way RCH-1 makes an unreached package visible statically.
Source code in src/symfonic/kernel/prompt_assembly.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | |