flip.flower.progress

Flower round-progress emission — the Flower twin of the NVFLARE round relay.

Emits the typed round facts (FLLogEvent) that back the hub’s round-aware activity feed. Facts only: the hub composes display text at serve time, so wording changes never require rebuilding FL images.

Every helper is best-effort — a telemetry failure is logged and must never break the strategy loop (matching flip.flower.metrics). Only the fl-server should import from this module; fl-clients hold no hub credentials.

flip.flower.strategy.FlipFedAvg wires these into the FedAvg hooks; app templates should subclass it rather than calling these directly.

Classes

RoundTelemetry

Per-run reply bookkeeping: who was dispatched, who answered, who fell out.

Functions

resolve_absent_site(→ str | None)

Name the single client that was dispatched a task but never returned a healthy reply.

report_round_started(→ None)

Report that the server dispatched the global model for a round.

report_client_result(→ bool)

Report one received client reply; returns whether the reply was healthy.

report_round_aggregated(→ None)

Report that a round's replies were aggregated.

Module Contents

flip.flower.progress.resolve_absent_site(dispatched_nodes: set[int], responded_nodes: set[int], site_by_node: dict[int, str]) str | None

Name the single client that was dispatched a task but never returned a healthy reply.

Flower stamps a placeholder src_node_id on the error reply it synthesises for an unreachable node, so a dead client cannot be recognised from its own reply. It can be recognised by elimination: the strategy knows which nodes it dispatched to, which answered, and (from earlier healthy replies) each node’s site name.

Returns None when the absence is ambiguous (several nodes silent at once) or the absent node never identified itself — guessing would put a real trust’s name on another trust’s failure.

Parameters:
  • dispatched_nodes – Node ids the round’s tasks were sent to.

  • responded_nodes – Node ids that returned a healthy reply this round.

  • site_by_node – Node id to site name, learned from healthy replies.

Returns:

The absent client’s site name, or None when unidentifiable.

Return type:

str | None

class flip.flower.progress.RoundTelemetry

Per-run reply bookkeeping: who was dispatched, who answered, who fell out.

Extracted from FlipFedAvg (which needs flwr to import) so CI can pin the two invariants that make crashed-client attribution safe:

  • absent clients are named only from sites learned off healthy replies (accumulated across the whole run) — never guessed from the placeholder identity on a synthesised error reply;

  • each phase keeps its own dispatch roster — a train strategy’s evaluate arm may sample a different cohort, and resolving evaluate absences against the train roster would put one trust’s name on another trust’s failure.

site_by_node: dict[int, str]
_dispatched_by_phase: dict[Phase, set[int]]
record_dispatch(phase: Phase, node_ids: set[int]) None

Record the node ids a phase’s tasks were just sent to (replaces the phase’s previous roster — each round samples afresh).

Parameters:
  • phase – Which strategy phase dispatched.

  • node_ids – The dst_node_id of every dispatched message.

dispatched_count(phase: Phase) int | None

How many clients the phase’s current round was dispatched to.

Parameters:

phase – Which strategy phase to count.

Returns:

The roster size, or None when the phase never dispatched.

Return type:

int | None

forward_replies(replies: list[flwr.common.message.Message], phase: Phase, server_round: int, model_id: str, flip: flip.FLIP) int

Forward every reply’s metrics, exception and round event to the hub.

Learns each healthy reply’s site, then names the one client the phase dispatched to that never returned a healthy reply (see resolve_absent_site) so its error is attributed to the right trust. fl-clients never reach the Central Hub directly — every forward happens here, server-side.

Parameters:
  • replies – The round’s reply Messages.

  • phase – The strategy phase these replies belong to.

  • server_round – Flower’s 1-based round number.

  • model_id – The FLIP model ID for the run.

  • flip – The FLIP instance used to reach the Central Hub.

Returns:

How many replies were healthy.

Return type:

int

flip.flower.progress.report_round_started(flip: flip.FLIP, model_id: str, server_round: int, total_rounds: int | None) None

Report that the server dispatched the global model for a round.

Parameters:
  • flip – The FLIP instance used to reach the Central Hub.

  • model_id – The FLIP model ID for the run.

  • server_round – Flower’s 1-based round number (already the wire contract).

  • total_rounds – The run’s round total, when known.

flip.flower.progress.report_client_result(msg: flwr.common.message.Message, server_round: int, model_id: str, flip: flip.FLIP) bool

Report one received client reply; returns whether the reply was healthy.

The boolean feeds the strategy’s returned-count, so a telemetry failure on a healthy reply still counts it as returned.

Parameters:
  • msg – A Flower reply Message from a client.

  • server_round – Flower’s 1-based round number.

  • model_id – The FLIP model ID for the run.

  • flip – The FLIP instance used to reach the Central Hub.

Returns:

True when the reply is healthy (no error), regardless of whether the telemetry post succeeded.

Return type:

bool

flip.flower.progress.report_round_aggregated(flip: flip.FLIP, model_id: str, server_round: int, returned: int | None, expected: int | None) None

Report that a round’s replies were aggregated.

Parameters:
  • flip – The FLIP instance used to reach the Central Hub.

  • model_id – The FLIP model ID for the run.

  • server_round – Flower’s 1-based round number.

  • returned – Healthy replies received this round, when known.

  • expected – Clients the round was dispatched to, when known.