flip.nvflare.components
FLIP Components module containing reusable FL components.
Components include event handlers, model locators, JSON generators, and persistence utilities.
- Exports:
ClientEventHandler: Client-side event handler
ServerEventHandler: Server-side event handler
PTModelLocator: PyTorch model locator
InitialPTModelLocator: PyTorch model locator for initial models with safehouse fallback
EvaluationPTModelLocator: PyTorch model locator for evaluation workflows (multi-model COLLECTION)
EvaluationModelLocator: Single-model checkpoint locator for Client-API evaluation (standard interface)
InitialCheckpointPTModelPersistor: Seeds the initial global model from a server-side backbone checkpoint
KeepOnlyVars: Include-only DXO filter (keep matching weights) — head-only per-round updates
TrimBroadcastVars: Server-side filter — broadcast only the trainable vars after round 0
TrimEvalBroadcastVars: Server-side filter — broadcast only the trainable vars for cross-site eval
ReconstructFullModel: Client-side filter — rebuild the full model from a trimmed broadcast
ReconstructFullModelForEval: Client-side filter — rebuild the full model for train AND eval
ValidationJsonGenerator: Validation results JSON generator
EvaluationJsonGenerator: Evaluation results JSON generator
PersistToS3AndCleanup: S3 persistence and cleanup component
PercentilePrivacy: Percentile-based privacy filter
StagePercentilePrivacy: Stage-aware percentile-based privacy filter
CleanupImages: Image cleanup executor
FlipAnalyticsBridge: Bridges Client API analytics events to FlipEvents.SEND_RESULT
ClientExceptionReporter: Reports client task failures to the FLIP hub
Submodules
- flip.nvflare.components.broadcast_trim_filter
- flip.nvflare.components.cleanup
- flip.nvflare.components.client_exception_reporter
- flip.nvflare.components.custom_percentile_privacy
- flip.nvflare.components.evaluation_json_generator
- flip.nvflare.components.flip_analytics_bridge
- flip.nvflare.components.flip_client_event_handler
- flip.nvflare.components.flip_server_event_handler
- flip.nvflare.components.keep_vars_filter
- flip.nvflare.components.persist_and_cleanup
- flip.nvflare.components.pt_model_locator
- flip.nvflare.components.pt_model_persistor
- flip.nvflare.components.reconstruct_model_filter
- flip.nvflare.components.stage_percentile_privacy
- flip.nvflare.components.validation_json_generator
Classes
Server-side |
|
Server-side |
|
Report client task failures to the FLIP hub without changing the result. |
|
Stock NVFLARE |
|
Evaluation results generator, recording both the metrics and the failed validate tasks. |
|
Translates NVFLARE analytics events fired by Client API scripts into |
|
ClientEventHandler is a generic component that handles system events triggered by nvflare |
|
ServerEventHandler is a generic component that handles system events triggered by nvflare |
|
Keep ONLY the weights whose key matches |
|
Locate uploaded checkpoint(s) for Client-API evaluation under the standard ModelLocator interface. |
|
Persistor that seeds the initial global model from a large backbone checkpoint |
|
Client-side |
|
Client-side |
|
Percentile-privacy filter that groups parameters by training stage before filtering. |
|
Stock NVFLARE |
Package Contents
- class flip.nvflare.components.TrimBroadcastVars(include_vars: str | None = None, data_kinds: list[str] | None = None)
Bases:
nvflare.apis.dxo_filter.DXOFilterServer-side
task_data_filter: after the first round, broadcast ONLY the trainable weights (those whose key matchesinclude_vars) instead of the full global model.The server→client mirror of
KeepOnlyVars(which trims the client→server update). For a frozen-backbone fine-tune the backbone is identical on every client after round 0, so re-broadcasting all ~759 MiB of it every round is pure waste: it saturates the wire and drives server-side heap growth (each broadcast is serialised into hundreds of MB-sized chunks). This trims the broadcast down to just the head after round 0; the client’sReconstructFullModelfilter merges it back onto the backbone delivered at round 0, so the trainer still receives a full state dict — no change to user training code is required.Round 0 is passed through UNTOUCHED so every client receives the backbone once (FLIP always runs
start_round == 0; the first round is where the backbone must ship). The filter is non-mutating: it builds a new dict rather than popping keys fromdxo.data, which on the server aliases the retained global model — popping would corrupt the server’s own weights.- include_vars = None
- pattern
- process_dxo(dxo: nvflare.apis.dxo_filter.DXO, shareable: nvflare.apis.shareable.Shareable, fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.apis.dxo_filter.DXO | None
- _trim_to_pattern(dxo: nvflare.apis.dxo_filter.DXO, fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.apis.dxo_filter.DXO | None
Trim
dxo.datato only the weight keys matchingself.pattern.Shared by the round-gated training filter and the always-trim evaluation variant (
TrimEvalBroadcastVars). Non-mutating on the caller’s dict — it builds a new dict — because on the serverdxo.dataaliases the retained global model, so popping keys would corrupt the server’s own weights.- Parameters:
dxo (DXO) – the outgoing broadcast DXO whose
data(a weights dict) is trimmed.fl_ctx (FLContext) – the FL context, used only for logging.
- Returns:
the DXO with its data trimmed to the matching (head) keys, or
None(leave the DXO untouched, i.e. broadcast the full model) when the regex matches no keys.- Return type:
DXO | None
- class flip.nvflare.components.TrimEvalBroadcastVars(include_vars: str | None = None, data_kinds: list[str] | None = None)
Bases:
TrimBroadcastVarsServer-side
task_data_filterfor thevalidatetask: broadcast ONLY the trainable head.The evaluation counterpart of
TrimBroadcastVars. Post-training cross-site validation (GlobalModelEval) re-broadcasts the full ~759 MiB global model to every client purely so it can be scored — but for a frozen-backbone fine-tune the backbone is byte-identical to the pretrained checkpoint each client already received at training round 0, so re-shipping it is pure waste. This trims thevalidatebroadcast down to just the head; the client’sReconstructFullModelForEvalmerges it back onto the backbone it cached during training, so the validator still receives a full state dict — no change to user validation code is required.Unlike
TrimBroadcastVars, the trim is unconditional: the cross-site-validation task carries noCURRENT_ROUNDheader (the round-gated parent would therefore never trim it), and the client always already holds the backbone by the time validation runs.- process_dxo(dxo: nvflare.apis.dxo_filter.DXO, shareable: nvflare.apis.shareable.Shareable, fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.apis.dxo_filter.DXO | None
- class flip.nvflare.components.CleanupImages
Bases:
nvflare.apis.executor.Executor- execute(task_name: str, shareable: nvflare.apis.shareable.Shareable, fl_ctx: nvflare.apis.fl_context.FLContext, abort_signal: nvflare.apis.signal.Signal) nvflare.apis.shareable.Shareable
- class flip.nvflare.components.ClientExceptionReporter
Bases:
nvflare.apis.filter.FilterReport client task failures to the FLIP hub without changing the result.
The model ID is job-scoped data and is therefore resolved exclusively from
meta.json['custom_props']['model_id']. The sending client is obtained from NVFLARE’s peer context, which is populated before server result filters run.- flip
- process(shareable: nvflare.apis.shareable.Shareable, fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.apis.shareable.Shareable
- class flip.nvflare.components.PercentilePrivacy(percentile=10, gamma=0.01, data_kinds: list[str] | None = None, off: bool = False)
Bases:
nvflare.app_common.filters.percentile_privacy.PercentilePrivacyStock NVFLARE
PercentilePrivacydifferential-privacy filter plus anofftoggle.Subclasses
nvflare.app_common.filters.percentile_privacy.PercentilePrivacyso the percentile/gamma filtering maths (Shokri and Shmatikov, “Privacy-preserving deep learning”, CCS ‘15) tracks upstream instead of drifting from a vendored copy. The sole FLIP addition isoff: it lets the filter stay wired intoconfig_fed_client.json’s result-filter chain while being switched on/off from config (e.g. running DP-on vs DP-off experiments) without adding or removing the component.- off = False
- process_dxo(dxo: nvflare.apis.dxo.DXO, shareable: nvflare.apis.shareable.Shareable, fl_ctx: nvflare.apis.fl_context.FLContext) None | nvflare.apis.dxo.DXO
Return the DXO unchanged when
off; otherwise delegate to stock filtering.- Parameters:
dxo (DXO) – Information from the client.
shareable (Shareable) – The shareable the DXO belongs to.
fl_ctx (FLContext) – Context provided by the workflow.
- Returns:
The unchanged DXO when off, else the stock-filtered DXO (or None if the stock guard clauses reject it).
- Return type:
None | DXO
- class flip.nvflare.components.EvaluationJsonGenerator(results_dir=PTConstants.EvalDir, json_file_name=PTConstants.EvalResultsFilename, failures_file_name=PTConstants.EvalFailuresFilename)
Bases:
flip.nvflare.components.validation_json_generator.ValidationJsonGeneratorEvaluation results generator, recording both the metrics and the failed validate tasks.
Deduped against FLIP’s
ValidationJsonGenerator: it inherits the manual-dispatch mechanism (the no-ophandle_event— ServerEventHandler driveshandle_evaluation_events) and the numpy-float JSON encoding / path-safe output. It differs in the results directory / file name, in tracking per-task failures, and in how results are keyed.Results shape. Each entry is keyed by data site, then by model name. Which controller drives the run decides where the model name comes from:
GlobalModelEval(evaluation_client_api) sends onevalidatetask per (model, client) and setsAppConstants.MODEL_OWNER, so results nest under it. Keying on the client alone would let each model’s metrics overwrite the previous model’s (FLIP#754).FLIP’s own
ModelEval(evaluation) sends every model in one task and sets noMODEL_OWNER; the evaluator returns its own model-keyed dict, which is stored as-is.
Failures. Both eval controllers fire
VALIDATION_RESULT_RECEIVEDbefore they inspect the result’s return code, so a failed or aborted task arrives here carrying no DXO. Each such task is recorded in_eval_failuresand written toPTConstants.EvalFailuresFilename, andall_tasks_failed()lets ServerEventHandler fail the run rather than report success on an empty results file (FLIP#754).- _eval_results: dict
- _eval_failures: list[dict] = []
- _failures_file_name = 'evaluation_failures.json'
- all_tasks_failed() bool
Whether every validate task this run produced a failure and none produced metrics.
- Returns:
- True when at least one task failed and none succeeded. False for a clean run, for a
partial failure (there are still results worth uploading), and for a job that ran no validate tasks at all — a training job’s END_RUN must not be reported as a failure.
- Return type:
bool
- _record_failure(fl_ctx: nvflare.apis.fl_context.FLContext, model_owner: str | None, data_client: str, return_code: str) None
Record one failed validate task so it survives into the results bundle.
- _record_result(model_owner: str | None, data_client: str, metrics: Any) None
Store one client’s metrics, nesting under the model name when the controller supplies one.
- _handle_validation_result(fl_ctx: nvflare.apis.fl_context.FLContext) None
Sort one received validate result into either the metrics store or the failure list.
- _write_json(fl_ctx: nvflare.apis.fl_context.FLContext, run_dir: str, file_name: str, payload: Any, description: str) None
Write one json artefact into the run’s evaluation results directory.
- handle_evaluation_events(event_type: str, fl_ctx: nvflare.apis.fl_context.FLContext) None
FLIP integration point — ServerEventHandler dispatches events here, not via handle_event.
- class flip.nvflare.components.FlipAnalyticsBridge
Bases:
nvflare.apis.fl_component.FLComponentTranslates NVFLARE analytics events fired by Client API scripts into FLIP’s
FlipEvents.SEND_RESULTfederated events.Trainer scripts written against the NVFLARE Client API publish metrics through
SummaryWriter/flare.log. TheInProcessClientAPIExecutorforwards those records onto the client process asANALYTIC_EVENT_TYPEevents. This widget rewraps each record as aDataKind.METRICSDXO and re-fires it underFlipEvents.SEND_RESULTwith federation scope, so the server-side controller (ScatterAndGather) keeps receiving metrics through the same pipeline thatsend_metrics_valueused to drive.- handle_event(event_type: str, fl_ctx: nvflare.apis.fl_context.FLContext) None
- class flip.nvflare.components.ClientEventHandler
Bases:
nvflare.apis.fl_component.FLComponentClientEventHandler is a generic component that handles system events triggered by nvflare or custom flip events. It executes logic inside its own event handler but may also call other component’s event handlers directly to help overcome the non-deterministic order in which nvflare handles events.
Args:
Raises:
- handle_event(event_type: str, fl_ctx: nvflare.apis.fl_context.FLContext) None
- class flip.nvflare.components.ServerEventHandler(model_id: str = '', validation_json_generator_id: str = 'json_generator', persist_and_cleanup_id: str = 'persist_and_cleanup', flip: flip.FLIP = FLIP())
Bases:
nvflare.apis.fl_component.FLComponentServerEventHandler is a generic component that handles system events triggered by nvflare or custom flip events. It executes logic inside its own event handler but may also call other component’s event handlers directly to overcome the non-deterministic order in which nvflare handles events i.e handling ValidationJsonGenerator component events.
- Parameters:
model_id (str, optional) – ID of the model. When empty, the model ID is resolved lazily from job metadata via
get_flip_model_idon first status update.validation_json_generator_id (str, optional) – Component ID for the validation JSON generator.
persist_and_cleanup_id (str, optional) – Component ID for the persist-and-cleanup component.
flip (FLIP, optional) – FLIP client instance.
- _model_id_fallback = ''
- _model_id: str | None = None
- validation_json_generator_id = 'json_generator'
- validation_json_generator = None
- persist_and_cleanup_id = 'persist_and_cleanup'
- persist_and_cleanup = None
- flip
- fatal_error = False
- final_status: flip.constants.ModelStatus | None = None
- _resolve_model_id(fl_ctx: nvflare.apis.fl_context.FLContext) str
Resolve model ID lazily from job metadata, falling back to the constructor arg.
- Parameters:
fl_ctx (FLContext) – The FL context for the current job.
- Returns:
The resolved model ID.
- Return type:
str
- _update_status(fl_ctx: nvflare.apis.fl_context.FLContext, status: flip.constants.ModelStatus | None) None
Resolve model ID lazily and update training status.
- Parameters:
fl_ctx (FLContext) – The FL context for the current job.
status (ModelStatus | None) – The new model status to set.
- _relay_round_event(fl_ctx: nvflare.apis.fl_context.FLContext, event: flip.schemas.FLLogEvent) None
Relay a stock round boundary to the hub as a typed fact.
Facts only — display text is composed hub-side. NVFLARE’s
CURRENT_ROUNDprop is 0-based; the wire contract is 1-based.ROUND_AGGREGATEDcounts come from the sticky props seeded here at round start (“0 of m”) and overwritten by the FLIP ScatterAndGather controller as it accepts client results.- Parameters:
fl_ctx (FLContext) – The FL context carrying the round props.
event (FLLogEvent) – ROUND_STARTED or ROUND_AGGREGATED.
- _expected_client_count(fl_ctx: nvflare.apis.fl_context.FLContext) int | None
Best-effort count of the clients targeted this round;
Nonewhen unavailable.- Parameters:
fl_ctx (FLContext) – The FL context to read the engine from.
- Returns:
len(engine.get_clients()), orNonewhen the engine cannot be read (the round then closes with the uncounted wording).- Return type:
int | None
- _evaluation_wholly_failed() bool
Whether this is an evaluation job in which every validate task failed.
Training jobs wire the base
ValidationJsonGenerator, which tracks no failures, so the isinstance check also serves as the “is this an evaluation job” test.- Returns:
True when the evaluation produced failures and no results at all.
- Return type:
bool
- _terminal_status(default: flip.constants.ModelStatus) flip.constants.ModelStatus
Resolve the run’s terminal status, most salient cause first.
A recorded fatal system error is the root cause and outranks everything. A user-requested abort outranks the evaluation failures it necessarily caused (aborting a run cancels its in-flight validate tasks, which must not be reported as ERROR). An evaluation in which every validate task failed outranks
default— otherwise a wholly failed run reports success on an empty results file (FLIP#754), or reports a trailing upload failure as its cause.- Parameters:
default (ModelStatus) – The status to use when no failure cause is recorded — the outcome of the upload itself.
- Returns:
The status to report to the hub.
- Return type:
- handle_event(event_type: str, fl_ctx: nvflare.apis.fl_context.FLContext) None
- __set_dependencies(fl_ctx: nvflare.apis.fl_context.FLContext) None
- class flip.nvflare.components.KeepOnlyVars(include_vars: str | None = None, data_kinds: list[str] | None = None)
Bases:
nvflare.apis.dxo_filter.DXOFilterKeep ONLY the weights whose key matches
include_vars(regex); drop the rest.The include-only inverse of NVFLARE’s
ExcludeVars(which is exclude-only). Used as a client-sidetask_result_filterto shrink a per-round update to just the trainable parameters: a frozen-backbone fine-tune sends only its head (~KB) instead of the full model (~759 MiB), which fixes the large-payload client→serverSubmitUpdatetimeout (FLIP#684).Keeping only the head is correct for a frozen backbone: the backbone is identical on every client after round 0, so it need not be re-aggregated; the server retains it from the round-0 global model (delivered by
InitialCheckpointPTModelPersistor) and the aggregator merges the head diff into it. The server’s WEIGHT_DIFF reconstruction is partial-safe (only keys present in the diff are applied), so a head-only update reconstructs correctly.- include_vars = None
- skip
- pattern
- process_dxo(dxo: nvflare.apis.dxo_filter.DXO, shareable: nvflare.apis.shareable.Shareable, fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.apis.dxo_filter.DXO | None
- class flip.nvflare.components.PersistToS3AndCleanup(model_id: str = '', persistor_id: str = AppConstants.DEFAULT_PERSISTOR_ID, flip: flip.FLIP = FLIP())
Bases:
nvflare.apis.fl_component.FLComponent- _model_id_fallback = ''
- _model_id: str | None = None
- persistor_id: str
- model_persistor: nvflare.app_opt.pt.file_model_persistor.PTFileModelPersistor | None = None
- model_inventory: dict
- model_dir: str = ''
- bucket_name: str = ''
- flip
- _resolve_model_id(fl_ctx: nvflare.apis.fl_context.FLContext) str
Resolve model ID lazily from job metadata, falling back to the constructor arg.
- Parameters:
fl_ctx (FLContext) – The FL context for the current job.
- Returns:
The resolved model ID.
- Return type:
str
- execute(fl_ctx: nvflare.apis.fl_context.FLContext) None
- upload_results_to_s3_bucket(fl_ctx: nvflare.apis.fl_context.FLContext) None
Uploads the final aggregated model and reports to an S3 bucket as a zip file.
- cleanup(fl_ctx: nvflare.apis.fl_context.FLContext) None
Cleans up the workspace by deleting the transfer and save directories for the model ID.
- class flip.nvflare.components.EvaluationModelLocator
Bases:
nvflare.app_common.abstract.model_locator.ModelLocatorLocate uploaded checkpoint(s) for Client-API evaluation under the standard ModelLocator interface.
Unlike
EvaluationPTModelLocator— which returns a singleDataKind.COLLECTIONDXO for the bespokeModelEvalcontroller — this exposes the stockget_model_names+locate_model(model_name, fl_ctx)contract so it drives NVFLARE’s stockCrossSiteModelEvalvalidate workflow directly. Each model named inconfig.json['models']becomes oneDataKind.WEIGHTSDXO that the server broadcasts to clients as a singleFLModelfor the Client-APIis_evaluate()path.The checkpoints are loaded server-side only — from the app’s
custom/directory when bundled (simulator / legacy bundling), else from the FL API’s de-bundled staging volume at<SERVER_CHECKPOINT_ROOT>/<model_id>/(production; same resolution order asEvaluationPTModelLocator). Clients never read the.ptfiles — they receive the weights over thevalidatetask.model_idis resolved lazily frommeta.json['custom_props'](recipe-built job types carry no component args).- models: dict | None = None
- _resolve_checkpoint_path(fl_ctx: nvflare.apis.fl_context.FLContext, app_dir: str, name: str, model_checkpoint: str)
Locate a checkpoint: bundled
custom/first, then the FL API’s shared staging volume.
- _load_models(fl_ctx: nvflare.apis.fl_context.FLContext) None
Read
config.json['models']and load each named checkpoint intoself.models.
- get_model_names(fl_ctx: nvflare.apis.fl_context.FLContext) list[str]
- locate_model(model_name: str, fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.apis.dxo.DXO | None
- class flip.nvflare.components.EvaluationPTModelLocator(exclude_vars=None, model_id: str = '')
Bases:
nvflare.app_common.abstract.model_locator.ModelLocator- models = None
- exclude_vars = None
- model_id = ''
- _resolve_checkpoint_path(fl_ctx: nvflare.apis.fl_context.FLContext, app_dir: str, name: str, model_checkpoint: str)
Locate a model checkpoint for server-side loading.
- Resolution order:
<app_dir>/custom/<checkpoint>— a checkpoint bundled in the app. Used by the local simulator (which copies the.ptintocustom/) and any legacy bundling.<SERVER_CHECKPOINT_ROOT>/<model_id>/<checkpoint>— the de-bundled checkpoint the FL API staged on the hub-local shared volume (production). Read straight from disk; the checkpoint is intentionally NOT shipped in the app bundle, so it never reaches the clients. This mirrors the Flower backend’s/app/srcshared mount.
Returns the resolved path, or
None(after logging an error) when neither exists.
- locate_model(fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.apis.dxo.DXO | None
- class flip.nvflare.components.InitialPTModelLocator(exclude_vars=None, model=None)
Bases:
nvflare.app_common.abstract.model_locator.ModelLocator- model = None
- exclude_vars = None
- get_model_names(fl_ctx: nvflare.apis.fl_context.FLContext) list[str]
- locate_model(model_name: str, fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.apis.dxo.DXO | None
- class flip.nvflare.components.PTModelLocator(exclude_vars=None, model=None)
Bases:
nvflare.app_common.abstract.model_locator.ModelLocator- model = None
- exclude_vars = None
- get_model_names(fl_ctx: nvflare.apis.fl_context.FLContext) list[str]
- locate_model(model_name: str, fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.apis.dxo.DXO | None
- class flip.nvflare.components.InitialCheckpointPTModelPersistor(model=None, model_id: str = '', **kwargs)
Bases:
nvflare.app_opt.pt.file_model_persistor.PTFileModelPersistorPersistor that seeds the initial global model from a large backbone checkpoint staged server-side, so the checkpoint never has to be bundled into the job app (bundling a ~759 MiB file collapses NVFLARE’s app-deploy to remote clients).
The backbone filename is declared in the job’s
config.jsonunderSERVER_CHECKPOINT. At load time it is resolved in this order:<app>/custom/<checkpoint>— a checkpoint bundled in the app. Used by the local simulator (which copies the file intocustom/) and any legacy bundling.<SERVER_CHECKPOINT_ROOT>/<model_id>/<checkpoint>— the de-bundled checkpoint the FL API staged on the hub-local shared volume (production). Read straight from disk; the checkpoint is intentionally NOT shipped in the app bundle, so it never reaches the clients. Mirrors the Flower backend’s/app/srcshared mount and the evalEvaluationPTModelLocator.
The resolved checkpoint is loaded (
strict=False) into theget_model()architecture so the round-0 global model that ScatterAndGather broadcasts carries the backbone plus freshly-initialised heads — a full state dict the clients can load. Clients therefore build only a bare architecture and receive all weights at round 0; they need no checkpoint file.When
config.jsondeclares noSERVER_CHECKPOINT(every other standard training job), this behaves exactly like the stockPTFileModelPersistor(initial weights from the model object), so it is a safe drop-in for the shared standard base app.- _model_id_arg = ''
- _resolve_backbone(fl_ctx: nvflare.apis.fl_context.FLContext)
Locate the declared backbone checkpoint, or
Noneif none is declared/found.
- load_model(fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.app_common.abstract.model.ModelLearnable
- class flip.nvflare.components.ReconstructFullModel(data_kinds: list[str] | None = None)
Bases:
nvflare.apis.dxo_filter.DXOFilterClient-side
task_data_filter: rebuild the full global model from a trimmed broadcast.Pairs with the server-side
TrimBroadcastVars. After round 0 the server broadcasts only the trainable head; this filter retains the full model received at round 0 (frozen backbone + head) and merges each subsequent round’s head into it, so the client’s executor always receives a full state dict. User training code is therefore unchanged — it never sees the head-only wire payload, only the reconstructed full model.Stateful: the retained model persists across rounds (the filter is a job-scoped component, instantiated once for the run). If a trimmed update arrives before any full model was cached — e.g. a client that (re)joined after round 0 and so never received the backbone — the filter raises, which NVFLARE surfaces as a task-data-filter error, failing the round loudly rather than silently training on a partial model.
- _full_weights: dict | None = None
- process_dxo(dxo: nvflare.apis.dxo_filter.DXO, shareable: nvflare.apis.shareable.Shareable, fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.apis.dxo_filter.DXO | None
- class flip.nvflare.components.ReconstructFullModelForEval(data_kinds: list[str] | None = None, evaluate_task_name: str = AppConstants.TASK_VALIDATION)
Bases:
ReconstructFullModelClient-side
task_data_filterfor BOTHtrainandvalidate: rebuild the full model.Extends
ReconstructFullModelto also reconstruct the full model for post-training cross-site validation (GlobalModelEval), whose head-only broadcast is produced by the server-sideTrimEvalBroadcastVars.Why one component on two tasks. The frozen backbone the client needs to reconstruct against is the one it received at training round 0 — in production the pretrained checkpoint is de-bundled server-side and never shipped to clients (see
InitialCheckpointPTModelPersistor), so the client’s ONLY copy of the backbone is the round-0 broadcast cached during training. NVFLARE builds a fresh filter instance per filter-chain occurrence, so the training cache is shared with the evaluation phase ONLY when the same component instance handles both tasks — i.e. one filter chain whosetasksare["train", "validate"]. This class is therefore wired onto both.Behaviour is dispatched by the current task name (set in
fl_ctxbefore client task-data filters run):train (or any non-evaluation task): delegate to
ReconstructFullModel— the proven round-gated cache-at-round-0 / merge-thereafter logic is unchanged.validate: merge the broadcast (head-only, or a full model if the server fell back) onto the retained full model from training and hand the validator a full state dict. Fails loudly if the client never cached a full model (it never trained → no backbone) or if the broadcast carries keys absent from the retained model (a mismatch that would otherwise validate wrong weights).
- _evaluate_task_name
- process_dxo(dxo: nvflare.apis.dxo_filter.DXO, shareable: nvflare.apis.shareable.Shareable, fl_ctx: nvflare.apis.fl_context.FLContext) nvflare.apis.dxo_filter.DXO | None
- class flip.nvflare.components.StagePercentilePrivacy(percentile=10, gamma=0.01, data_kinds: list[str] | None = None, off: bool = False)
Bases:
flip.nvflare.components.custom_percentile_privacy.PercentilePrivacyPercentile-privacy filter that groups parameters by training stage before filtering.
Subclasses FLIP’s
PercentilePrivacyto inherit its constructor (percentile/gamma/data_kinds/off) and — transitively — stock NVFLARE’s differential-privacy maths. It overridesprocess_dxoto compute the percentile cutoff per stage (fromFlipMetaKey.STAGE) rather than across all parameters at once, so each stage of a staged model (e.g. autoencoder then diffusion model) is filtered independently.- process_dxo(dxo: nvflare.apis.dxo.DXO, shareable: nvflare.apis.shareable.Shareable, fl_ctx: nvflare.apis.fl_context.FLContext) None | nvflare.apis.dxo.DXO
Compute the percentile on the abs delta_W, per stage.
Only shares the params whose absolute delta_W is greater than the percentile value, grouping parameters by the stages listed in
FlipMetaKey.STAGE.- Parameters:
dxo (DXO) – Information from the client.
shareable (Shareable) – The shareable the DXO belongs to.
fl_ctx (FLContext) – Context provided by the workflow.
- Returns:
The filtered DXO, the unchanged DXO (off / no stage info), or None (invalid gamma/percentile).
- Return type:
None | DXO
- class flip.nvflare.components.ValidationJsonGenerator
Bases:
nvflare.app_common.widgets.validation_json_generator.ValidationJsonGeneratorStock NVFLARE
ValidationJsonGenerator, dispatched manually by FLIP’s ServerEventHandler.Subclasses
nvflare.app_common.widgets.validation_json_generator.ValidationJsonGeneratorso the results accumulation (METRICS and COLLECTION / T2 leaf handling), numpy-float JSON encoding and path-safe output track upstream instead of a drifting vendored copy (the fork had only the METRICS branch and a plainjson.dump). The defaultresults_dir/json_file_nameare identical to stock, so the constructor is inherited unchanged.The only FLIP-specific behaviour is dispatch. NVFLARE would auto-handle events through
handle_event; FLIP instead routes them throughServerEventHandler, which callshandle_evaluation_events. Because this component is still registered (so itshandle_eventis auto-fired for every event),handle_eventis suppressed here — otherwise each validation result would be recorded twice — and the real work is delegated to stock’s implementation fromhandle_evaluation_events.- handle_event(event_type: str, fl_ctx: nvflare.apis.fl_context.FLContext) None
- handle_evaluation_events(event_type: str, fl_ctx: nvflare.apis.fl_context.FLContext) None
FLIP integration point — ServerEventHandler dispatches events here, not via handle_event.