289289SPARSE_MLA_FP8_ROUTE_ENV = "CPPMEGA_SPARSE_MLA_FP8_ROUTE"
290290SPARSE_MLA_FP8_BWD_ENV = "CPPMEGA_SPARSE_MLA_FP8_BWD"
291291MAMBA3_PATH_C_BWD_ENV = "CPPMEGA_MAMBA3_PATH_C_BWD"
292+ # Long-sequence safety gate for the FP8 Path C AUTO route. At seq>=2048 the
293+ # row-phased replay/recompute arenas plus the fp32-mandatory q_scale/kv_scale/
294+ # lse/state banks make FP8 Path C peak memory explode to ~159-171 GB (~5x the
295+ # bf16 Path C peak) and run slower than FP8 Path B. The AUTO route therefore
296+ # fails closed to the FP8 Path B reference baseline at long sequence lengths.
297+ # Override the threshold with CPPMEGA_FP8_PATH_C_MAX_SEQ_LEN (set 0 to disable
298+ # the gate entirely). The gate is AUTO-only: an explicit Path C runtime opt-in
299+ # (--use-path-c-direct-chain-runtime / --use-path-c-fused-train-block-runtime)
300+ # always bypasses it.
301+ FP8_PATH_C_MAX_SEQ_LEN_ENV = "CPPMEGA_FP8_PATH_C_MAX_SEQ_LEN"
302+ # Set by run_receipt() from the --use-path-c-*-runtime args so that downstream
303+ # gate evaluations driven by the (flag-less) TrainHybridTinyConfig still observe
304+ # the explicit operator override and bypass the AUTO long-seq gate.
305+ FP8_PATH_C_EXPLICIT_OVERRIDE_ENV = "CPPMEGA_FP8_PATH_C_EXPLICIT_RUNTIME_OVERRIDE"
306+ FP8_PATH_C_LONG_SEQ_GATE_DEFAULT = 2048
307+ FP8_PATH_C_LONG_SEQ_GATE_FALLBACK_DTYPE = FP8_PATH_B_DTYPE
308+ FP8_PATH_C_LONG_SEQ_GATE_STATUS = "fp8_path_c_auto_long_seq_gated_to_path_b"
292309FP8_PATH_C_RUNTIME_ENV: dict[str, str] = {
293310 SPARSE_MLA_FP8_ROUTE_ENV: "path_c",
294311 SPARSE_MLA_FP8_BWD_ENV: "path_c",
@@ -742,16 +759,147 @@ def optimizer_variant_payload(args: argparse.Namespace) -> dict[str, Any]:
742759 }
743760
744761
745- def fp8_path_c_route_requested (
762+ def fp8_path_c_dtype_requested (
746763 args: argparse.Namespace | TrainHybridTinyConfig,
747764) -> bool:
765+ """Return whether the CLI/config dtype names the FP8 Path C route.
766+
767+ This is the raw dtype check before the long-sequence AUTO safety gate. Use
768+ :func:`fp8_path_c_route_requested` for the effective decision that honors the
769+ gate.
770+ """
771+
748772 return str(getattr(args, "dtype", "")).strip().lower() == FP8_PATH_C_DTYPE
749773
750774
775+ def fp8_path_c_explicit_runtime_override(
776+ args: argparse.Namespace | TrainHybridTinyConfig,
777+ ) -> bool:
778+ """Return whether an explicit Path C runtime opt-in is present.
779+
780+ The two ``--use-path-c-*-runtime`` flags install the Path C value_and_grad
781+ runtime on the actual training critical path. They are explicit operator
782+ overrides, so they bypass the AUTO long-sequence safety gate.
783+
784+ The flags live only on the argparse namespace; ``run_receipt`` mirrors them
785+ into :data:`FP8_PATH_C_EXPLICIT_OVERRIDE_ENV` so the same decision holds when
786+ the gate is evaluated against the flag-less ``TrainHybridTinyConfig``.
787+ """
788+
789+ if (
790+ getattr(args, "use_path_c_direct_chain_runtime", False)
791+ or getattr(args, "use_path_c_fused_train_block_runtime", False)
792+ ):
793+ return True
794+ return os.environ.get(FP8_PATH_C_EXPLICIT_OVERRIDE_ENV, "").strip().lower() in {
795+ "1",
796+ "true",
797+ "yes",
798+ "on",
799+ }
800+
801+
802+ def fp8_path_c_max_seq_len() -> int:
803+ """Return the FP8 Path C AUTO long-sequence gate threshold.
804+
805+ Defaults to :data:`FP8_PATH_C_LONG_SEQ_GATE_DEFAULT` (2048). Override with the
806+ ``CPPMEGA_FP8_PATH_C_MAX_SEQ_LEN`` env var. A value of ``0`` (or any
807+ non-positive integer) disables the gate.
808+ """
809+
810+ raw = os.environ.get(FP8_PATH_C_MAX_SEQ_LEN_ENV)
811+ if raw is None or raw.strip() == "":
812+ return FP8_PATH_C_LONG_SEQ_GATE_DEFAULT
813+ try:
814+ value = int(raw)
815+ except ValueError as exc:
816+ raise ValueError(
817+ f"{FP8_PATH_C_MAX_SEQ_LEN_ENV} must be an integer sequence length"
818+ ) from exc
819+ return value
820+
821+
822+ def fp8_path_c_long_seq_gate(
823+ args: argparse.Namespace | TrainHybridTinyConfig,
824+ ) -> dict[str, Any]:
825+ """Evaluate the FP8 Path C AUTO long-sequence safety gate.
826+
827+ The gate only applies to the AUTO FP8 Path C dtype route. It fails closed to
828+ the FP8 Path B reference baseline when the configured sequence length reaches
829+ the threshold, because FP8 Path C peak memory explodes (~5x bf16 Path C) and
830+ runs slower than Path B at long sequence lengths. An explicit Path C runtime
831+ opt-in bypasses the gate, and a non-positive threshold disables it.
832+
833+ Returns a receipt-shaped payload. ``gated`` is True only when the route is
834+ actively being refused.
835+ """
836+
837+ dtype_requested = fp8_path_c_dtype_requested(args)
838+ explicit_override = fp8_path_c_explicit_runtime_override(args)
839+ threshold = fp8_path_c_max_seq_len()
840+ seq_len = int(getattr(args, "seq_len", 0) or 0)
841+ gate_enabled = threshold > 0
842+ gated = (
843+ dtype_requested
844+ and gate_enabled
845+ and not explicit_override
846+ and seq_len >= threshold
847+ )
848+ reason: str | None = None
849+ if gated:
850+ reason = (
851+ f"FP8 Path C AUTO route refused: seq_len={seq_len} >= "
852+ f"{FP8_PATH_C_MAX_SEQ_LEN_ENV}={threshold}. FP8 Path C peak memory "
853+ f"explodes (~5x bf16 Path C, 159-171 GB observed at seq=2048) and is "
854+ f"slower than FP8 Path B at long sequence lengths; falling back to the "
855+ f"FP8 Path B reference baseline. Pass an explicit Path C runtime "
856+ f"opt-in (--use-path-c-direct-chain-runtime / "
857+ f"--use-path-c-fused-train-block-runtime) or set "
858+ f"{FP8_PATH_C_MAX_SEQ_LEN_ENV}=0 to override."
859+ )
860+ return {
861+ "gated": gated,
862+ "dtype_requested": dtype_requested,
863+ "explicit_override": explicit_override,
864+ "gate_enabled": gate_enabled,
865+ "threshold": threshold,
866+ "seq_len": seq_len,
867+ "fallback_dtype": FP8_PATH_C_LONG_SEQ_GATE_FALLBACK_DTYPE if gated else None,
868+ "status": FP8_PATH_C_LONG_SEQ_GATE_STATUS if gated else "not_gated",
869+ "env": FP8_PATH_C_MAX_SEQ_LEN_ENV,
870+ "reason": reason,
871+ }
872+
873+
874+ def fp8_path_c_route_requested(
875+ args: argparse.Namespace | TrainHybridTinyConfig,
876+ ) -> bool:
877+ """Return the effective FP8 Path C route decision (AUTO gate applied).
878+
879+ Equals the raw dtype request unless the AUTO long-sequence safety gate fires,
880+ in which case the route fails closed to the FP8 Path B baseline.
881+ """
882+
883+ if not fp8_path_c_dtype_requested(args):
884+ return False
885+ return not fp8_path_c_long_seq_gate(args)["gated"]
886+
887+
888+ def fp8_path_c_route_gated_to_path_b(
889+ args: argparse.Namespace | TrainHybridTinyConfig,
890+ ) -> bool:
891+ """Return whether an FP8 Path C request was demoted to Path B by the gate."""
892+
893+ return fp8_path_c_dtype_requested(args) and fp8_path_c_long_seq_gate(args)["gated"]
894+
895+
751896def fp8_path_b_route_requested(
752897 args: argparse.Namespace | TrainHybridTinyConfig,
753898) -> bool:
754- return str(getattr(args, "dtype", "")).strip().lower() == FP8_PATH_B_DTYPE
899+ if str(getattr(args, "dtype", "")).strip().lower() == FP8_PATH_B_DTYPE:
900+ return True
901+ # A long-seq-gated FP8 Path C request fails closed onto the Path B baseline.
902+ return fp8_path_c_route_gated_to_path_b(args)
755903
756904
757905def fp8_training_route_requested(
@@ -1232,7 +1380,8 @@ def precision_route_payload(
12321380 args: argparse.Namespace | TrainHybridTinyConfig,
12331381) -> dict[str, Any]:
12341382 if fp8_path_b_route_requested(args):
1235- return {
1383+ long_seq_gate = fp8_path_c_long_seq_gate(args)
1384+ payload = {
12361385 "requested": FP8_PATH_B_DTYPE,
12371386 "kind": "fp8_path_b_reference_baseline",
12381387 "status": FP8_PATH_B_E2E_TRAINING_STATUS,
@@ -1246,6 +1395,14 @@ def precision_route_payload(
12461395 "hidden_wrapper_quantization_allowed": False,
12471396 "kernel_boundary_quantization_allowed": False,
12481397 }
1398+ if long_seq_gate["gated"]:
1399+ # The user asked for FP8 Path C but the AUTO long-seq gate demoted the
1400+ # route to the Path B reference baseline; record why for the receipt.
1401+ payload["requested"] = FP8_PATH_C_DTYPE
1402+ payload["kind"] = "fp8_path_c_auto_long_seq_gated_to_path_b"
1403+ payload["status"] = long_seq_gate["status"]
1404+ payload["fp8_path_c_long_seq_gate"] = long_seq_gate
1405+ return payload
12491406 if fp8_path_c_route_requested(args):
12501407 producer = sparse_mla_fp8_producer_payload(args)
12511408 producer_configured = bool(producer["configured"])
@@ -10861,6 +11018,13 @@ def run_receipt(
1086111018 local_gb10_route_fn: Callable[..., tuple[dict[str, Any], int]] | None = None,
1086211019 allocation_probe_fn: Callable[[], dict[str, Any]] | None = None,
1086311020) -> tuple[dict[str, Any], int]:
11021+ # Mirror the explicit Path C runtime opt-in onto the environment so the AUTO
11022+ # long-seq gate makes the same decision whether it is evaluated against the
11023+ # argparse namespace or the flag-less TrainHybridTinyConfig.
11024+ if fp8_path_c_explicit_runtime_override(args):
11025+ os.environ[FP8_PATH_C_EXPLICIT_OVERRIDE_ENV] = "1"
11026+ else:
11027+ os.environ.pop(FP8_PATH_C_EXPLICIT_OVERRIDE_ENV, None)
1086411028 if args.steps < 1:
1086511029 return (
1086611030 blocked_receipt(
@@ -10989,9 +11153,9 @@ def _run_existing_training(
1098911153 )
1099011154 return enforce_loss_decrease_requirement(args, receipt)
1099111155 with (
10992- fp8_path_b_kernel_policy(config ),
10993- fp8_path_c_kernel_policy(config ),
10994- fp8_path_c_stdio_suppressed(config ),
11156+ fp8_path_b_kernel_policy(args ),
11157+ fp8_path_c_kernel_policy(args ),
11158+ fp8_path_c_stdio_suppressed(args ),
1099511159 ):
1099611160 return local_gb10_route_fn(
1099711161 args,
@@ -11013,9 +11177,9 @@ def _run_existing_training(
1101311177 memory_before = metal_memory_payload()
1101411178 try:
1101511179 with (
11016- fp8_path_b_kernel_policy(config ),
11017- fp8_path_c_kernel_policy(config ),
11018- fp8_path_c_stdio_suppressed(config ),
11180+ fp8_path_b_kernel_policy(args ),
11181+ fp8_path_c_kernel_policy(args ),
11182+ fp8_path_c_stdio_suppressed(args ),
1101911183 ):
1102011184 if args.dry_run_json:
1102111185 train_payload = dry_run_payload_fn(
0 commit comments