mstar.model.submodule_base#
Classes
|
Unlike in regular ModelInputs, for LLMInputs we expect either input_ids or input_embeds to be set (but typically not both), and we require input_seq_len to be set (for cache planning). |
|
A step's outputs, split by how they are addressed. |
|
A step's row-addressed outputs ( |
|
What the worker knows about one request's inputs to this step. |
|
The batch's ``PerRequestState``s, resolved on first read. |
|
|
|
|
Base class for a model's compute units: defines the prepare_inputs → preprocess → forward(_batched) contract the engines drive. |
|
|
Engine-owned per-request state a submodule persists across forwards. |
|
- class mstar.model.submodule_base.ARNodeInputs(tensor_inputs=<factory>, kwargs=<factory>, resource_step_info=None, input_seq_len=0, input_ids=None, input_embeds=None, custom_pos_ids=None)[source]#
Bases:
NodeInputsUnlike in regular ModelInputs, for LLMInputs we expect either input_ids or input_embeds to be set (but typically not both), and we require input_seq_len to be set (for cache planning).
The tensor_inputs and kwargs dicts are still available for additional inputs as needed; but the main LLM inputs should be provided in the given dedicated fields.
- Parameters:
- classmethod collate(inputs_list, stacking_method=StackingMethod.NONE)[source]#
- Parameters:
inputs_list (list[ARNodeInputs])
- class mstar.model.submodule_base.ARNodeSubmodule[source]#
Bases:
NodeSubmodule- abstractmethod prepare_inputs(graph_walk, fwd_info, inputs, **kwargs)[source]#
- Parameters:
- Return type:
- abstractmethod preprocess(graph_walk, engine_inputs, inputs)[source]#
- Parameters:
graph_walk (str)
engine_inputs (ModelInputsFromEngine)
inputs (list[ARNodeInputs])
- Return type:
- split_inputs(graph_walk, fwd_info, inputs, start, end)[source]#
This walk’s inputs restricted to tokens
[start, end).Only the sequence-shaped fields are cut.
tensor_inputs,kwargsandresource_step_infoare opaque here, so a submodule carrying any of them says for itself how they are cut, or refuses to be cut at all.- Parameters:
graph_walk (str)
fwd_info (CurrentForwardPassInfo)
inputs (ARNodeInputs)
start (int)
end (int)
- Return type:
- class mstar.model.submodule_base.BatchedModelOutput(per_rid_outputs=<factory>, packed_outputs=<factory>, check_stop_buffers=None, row_outputs=None, row_request_ids=None)[source]#
Bases:
objectA step’s outputs, split by how they are addressed.
per_rid_outputsis keyed by request id.packed_outputsholds whole batch tensors a captured graph emitted under a__name__key, which a submodule’sunpack_packed_outputscuts per request.check_stop_buffersis what the stop check reads (default: the per-rid outputs, one device-to-host copy per tensor per request); a batch tensor with row i for request i makes it one copy.row_outputsdoes the same for the outputs: the engine clones each batch tensor once, not once per request, and hands out row views. OnlyEngine._collect_outputsreads it.The two dicts are never None, so readers on the critical path need no guard.
- Parameters:
- clone_check_stop_buffers()[source]#
A detached copy: the next replay overwrites a captured graph’s buffers before the stop check reads them.
- clone_row_outputs(num_rows)[source]#
One clone per row-addressed batch tensor, narrowed to the real rows so padding rows are never copied (see
clone_check_stop_buffers).
- classmethod coerce(output)[source]#
- Parameters:
output (BatchedModelOutput | dict[str, Any])
- Return type:
- class mstar.model.submodule_base.HostRows(request_ids, buffers)[source]#
Bases:
NamedTupleA step’s row-addressed outputs (
check_stop_buffers) on the host.Row i of every buffer belongs to
request_ids[i], the order the forward ran the requests in; a padded replay leaves extra rows past the real ones.
- class mstar.model.submodule_base.InputMetadata(stream_chunks=mappingproxy({}), dynamic_loop_iter_counts=mappingproxy({}))[source]#
Bases:
NamedTupleWhat the worker knows about one request’s inputs to this step.
Per step, unlike the per-request
CurrentForwardPassInfo. Build it with keywords: fields get added here rather than as new hook arguments.- Parameters:
stream_chunks (Mapping[str, StreamChunkInfo])
- stream_chunks: Mapping[str, StreamChunkInfo]#
Alias for field number 0
- class mstar.model.submodule_base.LazyRequestStates(submodule, rids)[source]#
Bases:
MappingThe batch’s ``PerRequestState``s, resolved on first read.
Only a couple of submodules read these, but the engine builds the view for every step of every node; materialising the dict there is bs dict inserts (and, for a padded step, bs
PerRequestStateallocations) per step that nothing usually looks at.- Parameters:
submodule (NodeSubmodule)
rids (Sequence[str])
- class mstar.model.submodule_base.ModelInputsFromEngine(request_ids: 'list[str]', per_request_info: 'dict[str, CurrentForwardPassInfo]', resources: 'dict[str, Resource]'=<factory>, piecewise_runners: "dict[str, 'PiecewiseCudaGraphRunner']"=<factory>, per_request_states: "'Mapping[str, PerRequestState] | None'"=None, step: 'SubmoduleStep | None' = None, captured: 'bool' = False, per_request_input_metadata: 'Mapping[str, InputMetadata]'=<factory>)[source]#
Bases:
object- Parameters:
per_request_info (dict[str, CurrentForwardPassInfo])
per_request_states (Mapping[str, PerRequestState] | None)
step (SubmoduleStep | None)
captured (bool)
per_request_input_metadata (Mapping[str, InputMetadata])
- property first_request_info#
unlike single_request_info, does not assert that there is only one request
- per_request_info: dict[str, CurrentForwardPassInfo]#
- per_request_input_metadata: Mapping[str, InputMetadata]#
- per_request_states: Mapping[str, PerRequestState] | None = None#
- property single_request_info#
asserts that there is only one request
- Type:
IMPORTANT
- step: SubmoduleStep | None = None#
- class mstar.model.submodule_base.NodeInputs(tensor_inputs: 'dict[str, torch.Tensor]'=<factory>, kwargs: 'dict' = <factory>, resource_step_info: 'Any | None' = None, input_seq_len: 'int' = 0)[source]#
Bases:
object- Parameters:
- class mstar.model.submodule_base.NodeSubmodule[source]#
-
Base class for a model’s compute units: defines the prepare_inputs → preprocess → forward(_batched) contract the engines drive.
- bind_node_resources(resources)[source]#
Receive the engine-built resources for this submodule’s node, and pass them down to every layer that calls one.
A layer body (attention, cross-attention) calls the resources directly —
attn.run,kv.write_kv,pos.apply_qk— so it needs its own references. It resolves them here, once at load, by the labels the model declared inget_node_resources; a layer that names a label this node doesn’t have fails at bind rather than in the middle of a forward.
- can_batch(batch, model_inputs)[source]#
- Parameters:
batch (ExecutingBatch)
model_inputs (list[NodeInputs])
- can_use_cuda_graphs(batch, model_inputs)[source]#
Return True if this submodule supports CUDA graphs for
batch.Default: derives from
get_cuda_graph_configs— if any declared config can replay for this batch’s graph_walk, CUDA graphs are supported. We checkcfg.replay_graph_walks(not justcfg.capture_graph_walk) so aliased walks — e.g. Qwen3-Omni’sprefill_audioreusing theprefill_textcapture, orprefill_visionreusing its own — are correctly admitted at the eligibility gate. The runner’s_config_foralready looks up byreplay_graph_walks; this keeps the gate consistent so aliased walks don’t silently fall through to the eager path.replay_graph_walksis always a superset of{capture_graph_walk}(seeCudaGraphConfig.__init__), so this never narrows what the previous code accepted — only widens it for configs that explicitly declared aliases.Subclasses can override to reject on batch shape / metadata (e.g. codec submodules that need homogeneous frame counts).
- Parameters:
batch (ExecutingBatch)
model_inputs (list[NodeInputs])
- Return type:
- cg_key_info(graph_walk, per_request_info, per_request_input_metadata=None, **kwargs)[source]#
Which of this walk’s capture buckets a batch belongs to.
A walk can be captured more than once when the batch’s shape is not the whole story — bagel captures decode twice, guidance on and off, because the two declare different segments over the same token count. The engine leases a slot before the step is declared, so it cannot read the answer off the step; it asks here instead.
Must equal the
additional_key_infoon the config that captured the bucket, and thecg_key_infothis batch’sdeclare_stepputs on its step. Disagreeing is not an error anywhere — it just misses the capture and runs eager — so derive both from one place.per_request_input_metadatais the batch’s per-step input facts (as onModelInputsFromEngine), e.g. for a bucket that follows a stream chunk.None (the default) means the walk has a single capture.
- Parameters:
graph_walk (str)
per_request_info (dict[str, CurrentForwardPassInfo])
per_request_input_metadata (Mapping[str, InputMetadata] | None)
kwargs (Any)
- Return type:
- check_stop(request_id, request_info, outputs)[source]#
Return the set of dynamic-loop names that should stop after this step.
Runs on the worker’s slow-postprocess path after
execute_batchreturns — never insideexecute_batch. Allowed to read tensor values (.item()/.cpu()) because by this point the GPU thread is no longer blocked by it.Stops returned here are deferred by one step: they apply to the worker’s next iter’s fast postprocess. The current in-flight step (already submitted under the assumption that the rid continues) will run for that rid and its output discarded — the standard 1-wasted-step cost for any stop signal.
Default: no stops.
- check_stop_batched(request_ids, request_infos, host_rows)[source]#
check_stopfor the whole batch at once, off the host rows.Optional. Returns rid -> loops to stop (rids with none left out), and must agree with
check_stopfor every rid. None, or a raise, falls back to the per-request path.
- cleanup_request(request_id)[source]#
Remove per-request state when a request completes. The engines call this on request removal; overrides with extra internal state should call super().
- Parameters:
request_id (str)
- declare_step(graph_walk, request_ids, inputs, slot_lease=None, piecewise_leases=None, **kwargs)[source]#
Declare this batch’s step for the runner to drive: which cache streams it touches, what spans they grow by, which plans back it, which streams fork, and what commits when it lands. The runner drives the declaration before
preprocessand commits it after the forward, so a declaring submodule keeps no plan or advance calls of its own. None means the submodule still plans and advances through the facade itself.request_idspairs positionally withinputs. Under a captured graph the batch is padded to the bucket’s shape, so it carries the padding rows’ ids too — declare their segments like any other row.slot_leaseis the slot this step will replay on, or None for an eager step. A submodule whose declaration differs between the two (cosmos3 packs both guidance branches into one plan for the captured shape) must key off this, not off its own capture key: the key says the batch could be captured, the lease says it was.piecewise_leasesnames the regions of this node that hold a slot for this step. Such a region declares, plans and commits its own work, so a resource it owns must be left out of this declaration.- Parameters:
- Return type:
SubmoduleStep | None
- filter_batched_output(request_info, outputs)[source]#
Drop keys a real request shouldn’t receive. A captured forward emits a fixed key set for graph compat, so the filtering happens here.
- abstractmethod forward(graph_walk, engine_inputs, **kwargs)[source]#
Pure tensor → NameToTensorList computation. Compilable + CUDA-graphable.
- forward_batched(graph_walk, engine_inputs, **kwargs)[source]#
Batched form of
forward: a multi-request batch’s outputs, per request or as aBatchedModelOutput. Override whencan_batchreturns True.- Parameters:
graph_walk (str)
engine_inputs (ModelInputsFromEngine)
- Return type:
- get_autocast_dtype()[source]#
Per-submodule autocast dtype override for the engine’s forward wrap. The engine consults this on each
execute_batchand uses the returned dtype instead of its own when non-None.Default:
None(inherit the engine’s autocast dtype). To turn autocast off for one specific submodule whose engine otherwise has it enabled, wrap the submodule’s forward withtorch.amp.autocast(enabled=False)— that path is engine-agnostic and doesn’t need this surface.- Return type:
dtype | None
- get_cuda_graph_configs(device, tp_world_size=1)[source]#
- Parameters:
device (torch.device)
tp_world_size (int)
- Return type:
- get_piecewise_cuda_graph_configs(device, autocast_dtype, tp_world_size=1)[source]#
Return the piecewise CUDA graph configs this submodule opts into.
autocast_dtypeis the engine’s autocast dtype — passed so a config’smake_static_inputscan allocate the hidden-state buffer in the dtype the captured region runs under (avoids a copy-time upcast at replay).A piecewise CUDA graph captures ONE inner callable of this submodule’s forward (e.g. a transformer block loop) as a CUDA graph while the surrounding compute stays eager. The engine builds one
PiecewiseCudaGraphRunnerper returned label and threads the runners intoModelInputsFromEngine.piecewise_runnersso the submodule’s forward can look them up by label:runner = engine_inputs.piecewise_runners.get(“block_loop”) if runner is not None and runner.can_run(bs):
out = runner.run(static_inputs={…}, request_ids=…, seq_lens=…)
A config’s
capture_fntakes onePiecewiseCallInputs, whoseengine_inputs.resourcescarries the node’s resources — so a resource call (sampling included) can live INSIDE the capture, reading params straight from buffers whose addresses are stable across replays instead of being hoisted out as static inputs. A region that touches a resource declares its work in the config’s owndeclare_step; the runner admits, plans and commits it per replay.Default: no piecewise graphs. Override to return
{label: PiecewiseCudaGraphConfig}; multiple labels capture multiple independent graphs (i.e., one per outer function to be graphed).- Parameters:
device (torch.device)
autocast_dtype (torch.dtype)
tp_world_size (int)
- Return type:
- postprocess(request_id, request_info, outputs, inputs=None, **kwargs)[source]#
Per-request postprocessing on the submodule outputs.
Runs on the GPU thread inside
execute_batch, after the forward (eager, batched, or CUDA-graph replay).inputsis the request’sprepare_inputsresult for this step, so a submodule can finish a step the captured graph could not hold (e.g. combine guidance branches and run a Python multistep scheduler against the step’s input latents).Keep it metadata-only where possible. Avoid reading tensor values —
.item()/.cpu()sync here block the GPU thread and forfeit the worker’s async-scheduling overlap; stop-condition decisions that need token values (e.g. EOS) belong incheck_stop. A captured-path tail that must read scheduler state is the sanctioned exception — it costs the same sync wherever it runs.- Typical uses:
rebind output names for graph routing (
outputs["text_inputs"] = outputs["new_token"]);drop keys on a per-request basis for static-capture submodules (e.g. Qwen3-Omni Thinker dropping
thinker_statesfor requests that don’t need audio);finish a captured step from
inputs(Cosmos3 denoise tail).
Modifies
outputsin-place; returns nothing.- Parameters:
request_id (str)
request_info (CurrentForwardPassInfo)
inputs (NodeInputs | None)
- abstractmethod prepare_inputs(graph_walk, fwd_info, inputs, **kwargs)[source]#
- Parameters:
- Return type:
- preprocess(graph_walk, engine_inputs, inputs)[source]#
- Parameters:
graph_walk (str)
engine_inputs (ModelInputsFromEngine)
inputs (list[NodeInputs])
- Return type:
- request_state(request_id)[source]#
The request’s state, created on first access.
- Parameters:
request_id (str)
- Return type:
- request_states: dict[str, PerRequestState]#
- split_batches_by_capture_key(graph_walk)[source]#
Whether the scheduler batches each
cg_key_infogroup apart.A batch spanning two groups matches no capture. Opt in when that batch runs worse than two captured ones, e.g. per-request eager; leave it off when a batched eager forward handles the mix well.
- split_inputs(graph_walk, fwd_info, inputs, start, end)[source]#
This walk’s inputs restricted to tokens
[start, end).inputsis the whole prompt, prepared once and sliced rather than re-derived, so anything read out of a resource while preparing is read once, before any part of it runs.- Parameters:
graph_walk (str)
fwd_info (CurrentForwardPassInfo)
inputs (NodeInputs)
start (int)
end (int)
- Return type:
- unpack_packed_outputs(static_output, request_ids, real_seq_lens, inputs, per_request_info)[source]#
Per-rid slicing for packed sentinels emitted by the captured graph.
Decode-style submodules emit per-rid entries inside the captured forward (one slice per request, fixed shape), so they don’t need this. Prefill-style submodules pack a (total_tokens, …) tensor whose per-request slice ends depend on real seq_lens — slicing has to happen post-replay, outside the captured region. Default no-ops; override and key off
static_outputsentinel names.
- class mstar.model.submodule_base.PerRequestState(tensors=<factory>, kwargs=<factory>, disag_shared_keys=<factory>)[source]#
Bases:
objectEngine-owned per-request state a submodule persists across forwards.
Submodules stash whatever a request’s later steps need (schedulers, conditioning latents, packing metadata) instead of keeping private
dict[request_id, ...]attributes. The engine owns the lifecycle: it injects the batch’s states viaModelInputsFromEngine.per_request_statesand drops a request’s state when the request is removed — no submodule cleanup code required.tensorsvskwargssplit by value kind: device tensors go intensors, everything else (numbers, dicts, scheduler objects) inkwargs.disag_shared_keysis reserved for PD disaggregation — marked keys would travel with the request (tensors via the tensor manager, kwargs with the forward-pass info); no engine implements the transfer yet.