mixle.task.sft_plan module

sft_planner – trace-SFT for generating tool plans with parser-gated validation.

The generative rung above distill_planner(). The step-students decompose by classifying the next action; this trains a small causal LM (LM) on serialized teacher traces with the prompt-masked SFT objective (LM.fit_pairs) so the whole plan is generated:

request \n=> tool(k=v; k=v) | tool(k=v) | done \n

Free-form generation needs a strict validation boundary. The emitted text must parse under the plan grammar, every tool must exist, and every required argument must be present. Anything else escalates to the teacher and the trace is harvested. The model may emit arbitrary text, but only verified plans leave the function.

What this adds over the step-students (and what it does not): one model covers every tool with variable-length plans and can generalize over entity values it did not see during training. It does not infer unsupported tool compositions from absent traces; those cases escalate for teacher handling and future data collection.

class GenerativePlanner(lm, codec, tools, teacher, plan_agreement, max_new=160, constrained=True, conf_floor=None, lm_config=<factory>, n_requests=0, n_escalated=0, harvested=<factory>)[source]

Bases: object

A plan-writing LM behind a parse-and-validate gate: only verified plans leave; the rest escalate.

Parameters:
lm: Any
codec: _CharCodec
tools: dict[str, ToolSpec]
teacher: Callable[[str], list[dict]]
plan_agreement: float
max_new: int = 160
constrained: bool = True
conf_floor: float | None = None
lm_config: dict
n_requests: int = 0
n_escalated: int = 0
harvested: list[tuple[str, list[dict]]]
try_plan(request)[source]

Generate, parse, validate (grammar + specs + copy-fidelity); None = must escalate.

With constrained=True (default) the decode itself runs inside the plan grammar (mixle.task.constrained.constrained_plan_decode()): malformed text and copy-drifted values are unrepresentable, and the parse/validate below is a pure backstop.

Parameters:

request (str)

Return type:

list[dict] | None

report()[source]
Return type:

dict[str, Any]

save(path)[source]

Persist the plan-writing LM (weights + builder config), codec, specs, and gates; load() restores.

Parameters:

path (str)

Return type:

str

classmethod load(path, teacher, *, device='cpu')[source]

Reconstitute a serving GenerativePlanner from save() output plus the teacher fallback.

Parameters:
Return type:

GenerativePlanner

sft_planner(teacher, requests, tools, *, holdout=0.2, seed=0, d_model=96, n_layer=3, n_head=4, block=192, epochs=30, lr=3e-3, device='cpu', constrained=True)[source]

Trace-SFT a small causal LM into a plan writer, verified on held-out requests.

Traces serialize as request\n=> tool(k=v; ...) | ... \n pairs; LM.fit_pairs trains with the prompt masked so only plan tokens carry loss; generation stops at newline. Held-out agreement is plan-level exact match (tools + required args, in order) on requests the LM never saw.

Parameters:
Return type:

GenerativePlanner