flwr-nightly 1.23.0.dev20251020__py3-none-any.whl → 1.23.0.dev20251021__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flwr-nightly might be problematic. Click here for more details.

flwr/cli/ls.py CHANGED
@@ -220,14 +220,14 @@ def _to_table(run_list: list[_RunListType]) -> Table:
220
220
 
221
221
  # Add columns
222
222
  table.add_column(
223
- Text("Run ID", justify="center"), style="bright_white", no_wrap=True
223
+ Text("Run ID", justify="center"), style="bright_black", no_wrap=True
224
224
  )
225
- table.add_column(Text("FAB", justify="center"), style="dim white")
225
+ table.add_column(Text("FAB", justify="center"), style="bright_black")
226
226
  table.add_column(Text("Status", justify="center"))
227
227
  table.add_column(Text("Elapsed", justify="center"), style="blue")
228
- table.add_column(Text("Created At", justify="center"), style="dim white")
229
- table.add_column(Text("Running At", justify="center"), style="dim white")
230
- table.add_column(Text("Finished At", justify="center"), style="dim white")
228
+ table.add_column(Text("Created At", justify="center"), style="bright_black")
229
+ table.add_column(Text("Running At", justify="center"), style="bright_black")
230
+ table.add_column(Text("Finished At", justify="center"), style="bright_black")
231
231
 
232
232
  for row in run_list:
233
233
  (
flwr/cli/supernode/ls.py CHANGED
@@ -32,7 +32,7 @@ from flwr.cli.config_utils import (
32
32
  process_loaded_project_config,
33
33
  validate_federation_in_project_config,
34
34
  )
35
- from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat
35
+ from flwr.common.constant import FAB_CONFIG_FILE, NOOP_FLWR_AID, CliOutputFormat
36
36
  from flwr.common.date import format_timedelta, isoformat8601_utc
37
37
  from flwr.common.logger import print_json_error, redirect_output, restore_output
38
38
  from flwr.proto.control_pb2 import ( # pylint: disable=E0611
@@ -73,13 +73,6 @@ def ls( # pylint: disable=R0914, R0913, R0917
73
73
  help="Enable verbose output",
74
74
  ),
75
75
  ] = False,
76
- dry_run: Annotated[
77
- bool,
78
- typer.Option(
79
- "--dry-run",
80
- help="Simulate the command without contacting any SuperNodes",
81
- ),
82
- ] = False,
83
76
  ) -> None:
84
77
  """List SuperNodes in the federation."""
85
78
  # Resolve command used (list or ls)
@@ -105,7 +98,7 @@ def ls( # pylint: disable=R0914, R0913, R0917
105
98
  channel = init_channel(app, federation_config, auth_plugin)
106
99
  stub = ControlStub(channel)
107
100
  typer.echo("📄 Listing all nodes...")
108
- formatted_nodes = _list_nodes(stub, dry_run=dry_run)
101
+ formatted_nodes = _list_nodes(stub)
109
102
  restore_output()
110
103
  if output_format == CliOutputFormat.JSON:
111
104
  Console().print_json(_to_json(formatted_nodes, verbose=verbose))
@@ -132,10 +125,10 @@ def ls( # pylint: disable=R0914, R0913, R0917
132
125
  captured_output.close()
133
126
 
134
127
 
135
- def _list_nodes(stub: ControlStub, dry_run: bool) -> list[_NodeListType]:
128
+ def _list_nodes(stub: ControlStub) -> list[_NodeListType]:
136
129
  """List all nodes."""
137
130
  with flwr_cli_grpc_exc_handler():
138
- res: ListNodesResponse = stub.ListNodes(ListNodesRequest(dry_run=dry_run))
131
+ res: ListNodesResponse = stub.ListNodes(ListNodesRequest())
139
132
 
140
133
  return _format_nodes(list(res.nodes_info), res.now)
141
134
 
@@ -185,12 +178,12 @@ def _to_table(nodes_info: list[_NodeListType], verbose: bool) -> Table:
185
178
 
186
179
  # Add columns
187
180
  table.add_column(
188
- Text("Node ID", justify="center"), style="bright_white", no_wrap=True
181
+ Text("Node ID", justify="center"), style="bright_black", no_wrap=True
189
182
  )
190
- table.add_column(Text("Owner", justify="center"), style="dim white")
183
+ table.add_column(Text("Owner", justify="center"))
191
184
  table.add_column(Text("Status", justify="center"))
192
185
  table.add_column(Text("Elapsed", justify="center"))
193
- table.add_column(Text("Status Changed @", justify="center"), style="dim white")
186
+ table.add_column(Text("Status Changed @", justify="center"), style="bright_black")
194
187
 
195
188
  for row in nodes_info:
196
189
  (
@@ -223,7 +216,7 @@ def _to_table(nodes_info: list[_NodeListType], verbose: bool) -> Table:
223
216
 
224
217
  formatted_row = (
225
218
  f"[bold]{node_id}[/bold]",
226
- f"{owner_aid}",
219
+ f"{owner_aid}" if owner_aid != NOOP_FLWR_AID else f"[dim]{owner_aid}[/dim]",
227
220
  f"[{status_style}]{status}",
228
221
  f"[cyan]{elapse_activated}[/cyan]" if status == "online" else "",
229
222
  time_at,
flwr/common/constant.py CHANGED
@@ -17,6 +17,8 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
+ import os
21
+
20
22
  TRANSPORT_TYPE_GRPC_BIDI = "grpc-bidi"
21
23
  TRANSPORT_TYPE_GRPC_RERE = "grpc-rere"
22
24
  TRANSPORT_TYPE_GRPC_ADAPTER = "grpc-adapter"
@@ -135,7 +137,9 @@ GC_THRESHOLD = 200_000_000 # 200 MB
135
137
  # Constants for Inflatable
136
138
  HEAD_BODY_DIVIDER = b"\x00"
137
139
  HEAD_VALUE_DIVIDER = " "
138
- MAX_ARRAY_CHUNK_SIZE = 20_971_520 # 20 MB
140
+ FLWR_PRIVATE_MAX_ARRAY_CHUNK_SIZE = int(
141
+ os.getenv("FLWR_PRIVATE_MAX_ARRAY_CHUNK_SIZE", "5242880")
142
+ ) # 5 MB
139
143
 
140
144
  # Constants for serialization
141
145
  INT64_MAX_VALUE = 9223372036854775807 # (1 << 63) - 1
@@ -144,8 +148,12 @@ INT64_MAX_VALUE = 9223372036854775807 # (1 << 63) - 1
144
148
  FLWR_APP_TOKEN_LENGTH = 128 # Length of the token used
145
149
 
146
150
  # Constants for object pushing and pulling
147
- MAX_CONCURRENT_PUSHES = 8 # Default maximum number of concurrent pushes
148
- MAX_CONCURRENT_PULLS = 8 # Default maximum number of concurrent pulls
151
+ FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PUSHES = int(
152
+ os.getenv("FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PUSHES", "2")
153
+ ) # Default maximum number of concurrent pushes
154
+ FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PULLS = int(
155
+ os.getenv("FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PULLS", "2")
156
+ ) # Default maximum number of concurrent pulls
149
157
  PULL_MAX_TIME = 7200 # Default maximum time to wait for pulling objects
150
158
  PULL_MAX_TRIES_PER_OBJECT = 500 # Default maximum number of tries to pull an object
151
159
  PULL_INITIAL_BACKOFF = 1 # Initial backoff time for pulling objects
@@ -299,5 +307,5 @@ class ExecPluginType:
299
307
 
300
308
 
301
309
  # Constants for No-op auth plugins
302
- NOOP_FLWR_AID = "sys_noauth"
310
+ NOOP_FLWR_AID = "<none>"
303
311
  NOOP_ACCOUNT_NAME = "sys_noauth"
@@ -25,10 +25,10 @@ from typing import Callable, Optional, TypeVar
25
25
  from flwr.proto.message_pb2 import ObjectTree # pylint: disable=E0611
26
26
 
27
27
  from .constant import (
28
+ FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PULLS,
29
+ FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PUSHES,
28
30
  HEAD_BODY_DIVIDER,
29
31
  HEAD_VALUE_DIVIDER,
30
- MAX_CONCURRENT_PULLS,
31
- MAX_CONCURRENT_PUSHES,
32
32
  PULL_BACKOFF_CAP,
33
33
  PULL_INITIAL_BACKOFF,
34
34
  PULL_MAX_TIME,
@@ -118,7 +118,7 @@ def push_objects(
118
118
  *,
119
119
  object_ids_to_push: Optional[set[str]] = None,
120
120
  keep_objects: bool = False,
121
- max_concurrent_pushes: int = MAX_CONCURRENT_PUSHES,
121
+ max_concurrent_pushes: int = FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PUSHES,
122
122
  ) -> None:
123
123
  """Push multiple objects to the servicer.
124
124
 
@@ -137,7 +137,7 @@ def push_objects(
137
137
  If `True`, the original objects will be kept in the `objects` dictionary
138
138
  after pushing. If `False`, they will be removed from the dictionary to avoid
139
139
  high memory usage.
140
- max_concurrent_pushes : int (default: MAX_CONCURRENT_PUSHES)
140
+ max_concurrent_pushes : int (default: FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PUSHES)
141
141
  The maximum number of concurrent pushes to perform.
142
142
  """
143
143
  lock = threading.Lock()
@@ -168,7 +168,7 @@ def push_object_contents_from_iterable(
168
168
  object_contents: Iterable[tuple[str, bytes]],
169
169
  push_object_fn: Callable[[str, bytes], None],
170
170
  *,
171
- max_concurrent_pushes: int = MAX_CONCURRENT_PUSHES,
171
+ max_concurrent_pushes: int = FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PUSHES,
172
172
  ) -> None:
173
173
  """Push multiple object contents to the servicer.
174
174
 
@@ -181,7 +181,7 @@ def push_object_contents_from_iterable(
181
181
  A function that takes an object ID and its content as bytes, and pushes
182
182
  it to the servicer. This function should raise `ObjectIdNotPreregisteredError`
183
183
  if the object ID is not pre-registered.
184
- max_concurrent_pushes : int (default: MAX_CONCURRENT_PUSHES)
184
+ max_concurrent_pushes : int (default: FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PUSHES)
185
185
  The maximum number of concurrent pushes to perform.
186
186
  """
187
187
 
@@ -210,7 +210,7 @@ def pull_objects( # pylint: disable=too-many-arguments,too-many-locals
210
210
  object_ids: list[str],
211
211
  pull_object_fn: Callable[[str], bytes],
212
212
  *,
213
- max_concurrent_pulls: int = MAX_CONCURRENT_PULLS,
213
+ max_concurrent_pulls: int = FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PULLS,
214
214
  max_time: Optional[float] = PULL_MAX_TIME,
215
215
  max_tries_per_object: Optional[int] = PULL_MAX_TRIES_PER_OBJECT,
216
216
  initial_backoff: float = PULL_INITIAL_BACKOFF,
@@ -227,7 +227,7 @@ def pull_objects( # pylint: disable=too-many-arguments,too-many-locals
227
227
  The function should raise `ObjectUnavailableError` if the object is not yet
228
228
  available, or `ObjectIdNotPreregisteredError` if the object ID is not
229
229
  pre-registered.
230
- max_concurrent_pulls : int (default: MAX_CONCURRENT_PULLS)
230
+ max_concurrent_pulls : int (default: FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PULLS)
231
231
  The maximum number of concurrent pulls to perform.
232
232
  max_time : Optional[float] (default: PULL_MAX_TIME)
233
233
  The maximum time to wait for all pulls to complete. If `None`, waits
@@ -442,7 +442,7 @@ def pull_and_inflate_object_from_tree( # pylint: disable=R0913
442
442
  confirm_object_received_fn: Callable[[str], None],
443
443
  *,
444
444
  return_type: type[T] = InflatableObject, # type: ignore
445
- max_concurrent_pulls: int = MAX_CONCURRENT_PULLS,
445
+ max_concurrent_pulls: int = FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PULLS,
446
446
  max_time: Optional[float] = PULL_MAX_TIME,
447
447
  max_tries_per_object: Optional[int] = PULL_MAX_TRIES_PER_OBJECT,
448
448
  initial_backoff: float = PULL_INITIAL_BACKOFF,
@@ -460,7 +460,7 @@ def pull_and_inflate_object_from_tree( # pylint: disable=R0913
460
460
  A function to confirm that the object has been received.
461
461
  return_type : type[T] (default: InflatableObject)
462
462
  The type of the object to return. Must be a subclass of `InflatableObject`.
463
- max_concurrent_pulls : int (default: MAX_CONCURRENT_PULLS)
463
+ max_concurrent_pulls : int (default: FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PULLS)
464
464
  The maximum number of concurrent pulls to perform.
465
465
  max_time : Optional[float] (default: PULL_MAX_TIME)
466
466
  The maximum time to wait for all pulls to complete. If `None`, waits
@@ -25,7 +25,7 @@ from typing import TYPE_CHECKING, Any, cast, overload
25
25
 
26
26
  import numpy as np
27
27
 
28
- from ..constant import MAX_ARRAY_CHUNK_SIZE, SType
28
+ from ..constant import FLWR_PRIVATE_MAX_ARRAY_CHUNK_SIZE, SType
29
29
  from ..inflatable import (
30
30
  InflatableObject,
31
31
  add_header_to_object_body,
@@ -272,8 +272,8 @@ class Array(InflatableObject):
272
272
  chunks: list[tuple[str, InflatableObject]] = []
273
273
  # memoryview allows for zero-copy slicing
274
274
  data_view = memoryview(self.data)
275
- for start in range(0, len(data_view), MAX_ARRAY_CHUNK_SIZE):
276
- end = min(start + MAX_ARRAY_CHUNK_SIZE, len(data_view))
275
+ for start in range(0, len(data_view), FLWR_PRIVATE_MAX_ARRAY_CHUNK_SIZE):
276
+ end = min(start + FLWR_PRIVATE_MAX_ARRAY_CHUNK_SIZE, len(data_view))
277
277
  ac = ArrayChunk(data_view[start:end])
278
278
  chunks.append((ac.object_id, ac))
279
279
 
flwr/proto/control_pb2.py CHANGED
@@ -19,7 +19,7 @@ from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
19
19
  from flwr.proto import node_pb2 as flwr_dot_proto_dot_node__pb2
20
20
 
21
21
 
22
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x66lwr/proto/control.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x1a\x66lwr/proto/transport.proto\x1a\x1b\x66lwr/proto/recorddict.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x15\x66lwr/proto/node.proto\"\xfa\x01\n\x0fStartRunRequest\x12\x1c\n\x03\x66\x61\x62\x18\x01 \x01(\x0b\x32\x0f.flwr.proto.Fab\x12H\n\x0foverride_config\x18\x02 \x03(\x0b\x32/.flwr.proto.StartRunRequest.OverrideConfigEntry\x12\x34\n\x12\x66\x65\x64\x65ration_options\x18\x03 \x01(\x0b\x32\x18.flwr.proto.ConfigRecord\x1aI\n\x13OverrideConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"2\n\x10StartRunResponse\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"<\n\x11StreamLogsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x17\n\x0f\x61\x66ter_timestamp\x18\x02 \x01(\x01\"B\n\x12StreamLogsResponse\x12\x12\n\nlog_output\x18\x01 \x01(\t\x12\x18\n\x10latest_timestamp\x18\x02 \x01(\x01\"1\n\x0fListRunsRequest\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"\x9d\x01\n\x10ListRunsResponse\x12;\n\x08run_dict\x18\x01 \x03(\x0b\x32).flwr.proto.ListRunsResponse.RunDictEntry\x12\x0b\n\x03now\x18\x02 \x01(\t\x1a?\n\x0cRunDictEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run:\x02\x38\x01\"\x18\n\x16GetLoginDetailsRequest\"\x8b\x01\n\x17GetLoginDetailsResponse\x12\x12\n\nauthn_type\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65vice_code\x18\x02 \x01(\t\x12!\n\x19verification_uri_complete\x18\x03 \x01(\t\x12\x12\n\nexpires_in\x18\x04 \x01(\x03\x12\x10\n\x08interval\x18\x05 \x01(\x03\"+\n\x14GetAuthTokensRequest\x12\x13\n\x0b\x64\x65vice_code\x18\x01 \x01(\t\"D\n\x15GetAuthTokensResponse\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x01 \x01(\t\x12\x15\n\rrefresh_token\x18\x02 \x01(\t\" \n\x0eStopRunRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"\"\n\x0fStopRunResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\"&\n\x14PullArtifactsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"1\n\x15PullArtifactsResponse\x12\x10\n\x03url\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x06\n\x04_url\")\n\x13RegisterNodeRequest\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\"8\n\x14RegisterNodeResponse\x12\x14\n\x07node_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\n\n\x08_node_id\"(\n\x15UnregisterNodeRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x04\"\x18\n\x16UnregisterNodeResponse\"#\n\x10ListNodesRequest\x12\x0f\n\x07\x64ry_run\x18\x01 \x01(\x08\"J\n\x11ListNodesResponse\x12(\n\nnodes_info\x18\x01 \x03(\x0b\x32\x14.flwr.proto.NodeInfo\x12\x0b\n\x03now\x18\x02 \x01(\t2\xbc\x06\n\x07\x43ontrol\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x12\x44\n\x07StopRun\x12\x1a.flwr.proto.StopRunRequest\x1a\x1b.flwr.proto.StopRunResponse\"\x00\x12O\n\nStreamLogs\x12\x1d.flwr.proto.StreamLogsRequest\x1a\x1e.flwr.proto.StreamLogsResponse\"\x00\x30\x01\x12G\n\x08ListRuns\x12\x1b.flwr.proto.ListRunsRequest\x1a\x1c.flwr.proto.ListRunsResponse\"\x00\x12\\\n\x0fGetLoginDetails\x12\".flwr.proto.GetLoginDetailsRequest\x1a#.flwr.proto.GetLoginDetailsResponse\"\x00\x12V\n\rGetAuthTokens\x12 .flwr.proto.GetAuthTokensRequest\x1a!.flwr.proto.GetAuthTokensResponse\"\x00\x12V\n\rPullArtifacts\x12 .flwr.proto.PullArtifactsRequest\x1a!.flwr.proto.PullArtifactsResponse\"\x00\x12S\n\x0cRegisterNode\x12\x1f.flwr.proto.RegisterNodeRequest\x1a .flwr.proto.RegisterNodeResponse\"\x00\x12Y\n\x0eUnregisterNode\x12!.flwr.proto.UnregisterNodeRequest\x1a\".flwr.proto.UnregisterNodeResponse\"\x00\x12J\n\tListNodes\x12\x1c.flwr.proto.ListNodesRequest\x1a\x1d.flwr.proto.ListNodesResponse\"\x00\x62\x06proto3')
22
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x66lwr/proto/control.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x1a\x66lwr/proto/transport.proto\x1a\x1b\x66lwr/proto/recorddict.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x15\x66lwr/proto/node.proto\"\xfa\x01\n\x0fStartRunRequest\x12\x1c\n\x03\x66\x61\x62\x18\x01 \x01(\x0b\x32\x0f.flwr.proto.Fab\x12H\n\x0foverride_config\x18\x02 \x03(\x0b\x32/.flwr.proto.StartRunRequest.OverrideConfigEntry\x12\x34\n\x12\x66\x65\x64\x65ration_options\x18\x03 \x01(\x0b\x32\x18.flwr.proto.ConfigRecord\x1aI\n\x13OverrideConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"2\n\x10StartRunResponse\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"<\n\x11StreamLogsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x17\n\x0f\x61\x66ter_timestamp\x18\x02 \x01(\x01\"B\n\x12StreamLogsResponse\x12\x12\n\nlog_output\x18\x01 \x01(\t\x12\x18\n\x10latest_timestamp\x18\x02 \x01(\x01\"1\n\x0fListRunsRequest\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"\x9d\x01\n\x10ListRunsResponse\x12;\n\x08run_dict\x18\x01 \x03(\x0b\x32).flwr.proto.ListRunsResponse.RunDictEntry\x12\x0b\n\x03now\x18\x02 \x01(\t\x1a?\n\x0cRunDictEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run:\x02\x38\x01\"\x18\n\x16GetLoginDetailsRequest\"\x8b\x01\n\x17GetLoginDetailsResponse\x12\x12\n\nauthn_type\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65vice_code\x18\x02 \x01(\t\x12!\n\x19verification_uri_complete\x18\x03 \x01(\t\x12\x12\n\nexpires_in\x18\x04 \x01(\x03\x12\x10\n\x08interval\x18\x05 \x01(\x03\"+\n\x14GetAuthTokensRequest\x12\x13\n\x0b\x64\x65vice_code\x18\x01 \x01(\t\"D\n\x15GetAuthTokensResponse\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x01 \x01(\t\x12\x15\n\rrefresh_token\x18\x02 \x01(\t\" \n\x0eStopRunRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"\"\n\x0fStopRunResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\"&\n\x14PullArtifactsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"1\n\x15PullArtifactsResponse\x12\x10\n\x03url\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x06\n\x04_url\")\n\x13RegisterNodeRequest\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\"8\n\x14RegisterNodeResponse\x12\x14\n\x07node_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\n\n\x08_node_id\"(\n\x15UnregisterNodeRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x04\"\x18\n\x16UnregisterNodeResponse\"\x12\n\x10ListNodesRequest\"J\n\x11ListNodesResponse\x12(\n\nnodes_info\x18\x01 \x03(\x0b\x32\x14.flwr.proto.NodeInfo\x12\x0b\n\x03now\x18\x02 \x01(\t2\xbc\x06\n\x07\x43ontrol\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x12\x44\n\x07StopRun\x12\x1a.flwr.proto.StopRunRequest\x1a\x1b.flwr.proto.StopRunResponse\"\x00\x12O\n\nStreamLogs\x12\x1d.flwr.proto.StreamLogsRequest\x1a\x1e.flwr.proto.StreamLogsResponse\"\x00\x30\x01\x12G\n\x08ListRuns\x12\x1b.flwr.proto.ListRunsRequest\x1a\x1c.flwr.proto.ListRunsResponse\"\x00\x12\\\n\x0fGetLoginDetails\x12\".flwr.proto.GetLoginDetailsRequest\x1a#.flwr.proto.GetLoginDetailsResponse\"\x00\x12V\n\rGetAuthTokens\x12 .flwr.proto.GetAuthTokensRequest\x1a!.flwr.proto.GetAuthTokensResponse\"\x00\x12V\n\rPullArtifacts\x12 .flwr.proto.PullArtifactsRequest\x1a!.flwr.proto.PullArtifactsResponse\"\x00\x12S\n\x0cRegisterNode\x12\x1f.flwr.proto.RegisterNodeRequest\x1a .flwr.proto.RegisterNodeResponse\"\x00\x12Y\n\x0eUnregisterNode\x12!.flwr.proto.UnregisterNodeRequest\x1a\".flwr.proto.UnregisterNodeResponse\"\x00\x12J\n\tListNodes\x12\x1c.flwr.proto.ListNodesRequest\x1a\x1d.flwr.proto.ListNodesResponse\"\x00\x62\x06proto3')
23
23
 
24
24
  _globals = globals()
25
25
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -71,9 +71,9 @@ if _descriptor._USE_C_DESCRIPTORS == False:
71
71
  _globals['_UNREGISTERNODERESPONSE']._serialized_start=1397
72
72
  _globals['_UNREGISTERNODERESPONSE']._serialized_end=1421
73
73
  _globals['_LISTNODESREQUEST']._serialized_start=1423
74
- _globals['_LISTNODESREQUEST']._serialized_end=1458
75
- _globals['_LISTNODESRESPONSE']._serialized_start=1460
76
- _globals['_LISTNODESRESPONSE']._serialized_end=1534
77
- _globals['_CONTROL']._serialized_start=1537
78
- _globals['_CONTROL']._serialized_end=2365
74
+ _globals['_LISTNODESREQUEST']._serialized_end=1441
75
+ _globals['_LISTNODESRESPONSE']._serialized_start=1443
76
+ _globals['_LISTNODESRESPONSE']._serialized_end=1517
77
+ _globals['_CONTROL']._serialized_start=1520
78
+ _globals['_CONTROL']._serialized_end=2348
79
79
  # @@protoc_insertion_point(module_scope)
@@ -279,13 +279,8 @@ global___UnregisterNodeResponse = UnregisterNodeResponse
279
279
 
280
280
  class ListNodesRequest(google.protobuf.message.Message):
281
281
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
282
- DRY_RUN_FIELD_NUMBER: builtins.int
283
- dry_run: builtins.bool
284
282
  def __init__(self,
285
- *,
286
- dry_run: builtins.bool = ...,
287
283
  ) -> None: ...
288
- def ClearField(self, field_name: typing_extensions.Literal["dry_run",b"dry_run"]) -> None: ...
289
284
  global___ListNodesRequest = ListNodesRequest
290
285
 
291
286
  class ListNodesResponse(google.protobuf.message.Message):
@@ -262,6 +262,7 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
262
262
  node_id: self.nodes[node_id].online_until
263
263
  for node_id in dst_node_ids
264
264
  if node_id in self.nodes
265
+ and self.nodes[node_id].status != NodeStatus.UNREGISTERED
265
266
  },
266
267
  current_time=current,
267
268
  )
@@ -380,7 +381,10 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
380
381
  )
381
382
 
382
383
  node.status = NodeStatus.UNREGISTERED
383
- node.unregistered_at = now().isoformat()
384
+ current = now()
385
+ node.unregistered_at = current.isoformat()
386
+ # Set online_until to current timestamp on deletion, if it is in the future
387
+ node.online_until = min(node.online_until, current.timestamp())
384
388
 
385
389
  def get_nodes(self, run_id: int) -> set[int]:
386
390
  """Return all available nodes.
@@ -18,11 +18,10 @@
18
18
  # pylint: disable=too-many-lines
19
19
 
20
20
  import json
21
- import re
22
21
  import secrets
23
22
  import sqlite3
24
23
  from collections.abc import Sequence
25
- from logging import DEBUG, ERROR, WARNING
24
+ from logging import ERROR, WARNING
26
25
  from typing import Any, Optional, Union, cast
27
26
 
28
27
  from flwr.common import Context, Message, Metadata, log, now
@@ -52,6 +51,7 @@ from flwr.proto.recorddict_pb2 import RecordDict as ProtoRecordDict
52
51
  # pylint: enable=E0611
53
52
  from flwr.server.utils.validator import validate_message
54
53
  from flwr.supercore.constant import NodeStatus
54
+ from flwr.supercore.sqlite_mixin import SqliteMixin
55
55
 
56
56
  from .linkstate import LinkState
57
57
  from .utils import (
@@ -183,95 +183,25 @@ CREATE TABLE IF NOT EXISTS token_store (
183
183
  );
184
184
  """
185
185
 
186
- DictOrTuple = Union[tuple[Any, ...], dict[str, Any]]
187
186
 
188
-
189
- class SqliteLinkState(LinkState): # pylint: disable=R0904
187
+ class SqliteLinkState(LinkState, SqliteMixin): # pylint: disable=R0904
190
188
  """SQLite-based LinkState implementation."""
191
189
 
192
- def __init__(
193
- self,
194
- database_path: str,
195
- ) -> None:
196
- """Initialize an SqliteLinkState.
197
-
198
- Parameters
199
- ----------
200
- database : (path-like object)
201
- The path to the database file to be opened. Pass ":memory:" to open
202
- a connection to a database that is in RAM, instead of on disk.
203
- """
204
- self.database_path = database_path
205
- self.conn: Optional[sqlite3.Connection] = None
206
-
207
190
  def initialize(self, log_queries: bool = False) -> list[tuple[str]]:
208
- """Create tables if they don't exist yet.
209
-
210
- Parameters
211
- ----------
212
- log_queries : bool
213
- Log each query which is executed.
214
-
215
- Returns
216
- -------
217
- list[tuple[str]]
218
- The list of all tables in the DB.
219
- """
220
- self.conn = sqlite3.connect(self.database_path)
221
- self.conn.execute("PRAGMA foreign_keys = ON;")
222
- self.conn.row_factory = dict_factory
223
- if log_queries:
224
- self.conn.set_trace_callback(lambda query: log(DEBUG, query))
225
- cur = self.conn.cursor()
226
-
227
- # Create each table if not exists queries
228
- cur.execute(SQL_CREATE_TABLE_RUN)
229
- cur.execute(SQL_CREATE_TABLE_LOGS)
230
- cur.execute(SQL_CREATE_TABLE_CONTEXT)
231
- cur.execute(SQL_CREATE_TABLE_MESSAGE_INS)
232
- cur.execute(SQL_CREATE_TABLE_MESSAGE_RES)
233
- cur.execute(SQL_CREATE_TABLE_NODE)
234
- cur.execute(SQL_CREATE_TABLE_PUBLIC_KEY)
235
- cur.execute(SQL_CREATE_TABLE_TOKEN_STORE)
236
- cur.execute(SQL_CREATE_INDEX_ONLINE_UNTIL)
237
- cur.execute(SQL_CREATE_INDEX_OWNER_AID)
238
- res = cur.execute("SELECT name FROM sqlite_schema;")
239
- return res.fetchall()
240
-
241
- def query(
242
- self,
243
- query: str,
244
- data: Optional[Union[Sequence[DictOrTuple], DictOrTuple]] = None,
245
- ) -> list[dict[str, Any]]:
246
- """Execute a SQL query."""
247
- if self.conn is None:
248
- raise AttributeError("LinkState is not initialized.")
249
-
250
- if data is None:
251
- data = []
252
-
253
- # Clean up whitespace to make the logs nicer
254
- query = re.sub(r"\s+", " ", query)
255
-
256
- try:
257
- with self.conn:
258
- if (
259
- len(data) > 0
260
- and isinstance(data, (tuple, list))
261
- and isinstance(data[0], (tuple, dict))
262
- ):
263
- rows = self.conn.executemany(query, data)
264
- else:
265
- rows = self.conn.execute(query, data)
266
-
267
- # Extract results before committing to support
268
- # INSERT/UPDATE ... RETURNING
269
- # style queries
270
- result = rows.fetchall()
271
- except KeyError as exc:
272
- log(ERROR, {"query": query, "data": data, "exception": exc})
273
-
274
- return result
191
+ """Connect to the DB, enable FK support, and create tables if needed."""
192
+ return self._ensure_initialized(
193
+ SQL_CREATE_TABLE_RUN,
194
+ SQL_CREATE_TABLE_LOGS,
195
+ SQL_CREATE_TABLE_CONTEXT,
196
+ SQL_CREATE_TABLE_MESSAGE_INS,
197
+ SQL_CREATE_TABLE_MESSAGE_RES,
198
+ SQL_CREATE_TABLE_NODE,
199
+ SQL_CREATE_TABLE_PUBLIC_KEY,
200
+ SQL_CREATE_TABLE_TOKEN_STORE,
201
+ SQL_CREATE_INDEX_ONLINE_UNTIL,
202
+ SQL_CREATE_INDEX_OWNER_AID,
203
+ log_queries=log_queries,
204
+ )
275
205
 
276
206
  def store_message_ins(self, message: Message) -> Optional[str]:
277
207
  """Store one Message."""
@@ -490,11 +420,12 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
490
420
  sint_node_id = convert_uint64_to_sint64(in_message.metadata.dst_node_id)
491
421
  dst_node_ids.add(sint_node_id)
492
422
  query = f"""
493
- SELECT node_id, online_until
494
- FROM node
495
- WHERE node_id IN ({",".join(["?"] * len(dst_node_ids))});
496
- """
497
- rows = self.query(query, tuple(dst_node_ids))
423
+ SELECT node_id, online_until
424
+ FROM node
425
+ WHERE node_id IN ({",".join(["?"] * len(dst_node_ids))})
426
+ AND status != ?
427
+ """
428
+ rows = self.query(query, tuple(dst_node_ids) + (NodeStatus.UNREGISTERED,))
498
429
  tmp_ret_dict = check_node_availability_for_in_message(
499
430
  inquired_in_message_ids=message_ids,
500
431
  found_in_message_dict=found_message_ins_dict,
@@ -662,13 +593,17 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
662
593
 
663
594
  query = """
664
595
  UPDATE node
665
- SET status = ?, unregistered_at = ?
596
+ SET status = ?, unregistered_at = ?,
597
+ online_until = IIF(online_until > ?, ?, online_until)
666
598
  WHERE node_id = ? AND status != ? AND owner_aid = ?
667
599
  RETURNING node_id
668
600
  """
601
+ current = now()
669
602
  params = (
670
603
  NodeStatus.UNREGISTERED,
671
- now().isoformat(),
604
+ current.isoformat(),
605
+ current.timestamp(),
606
+ current.timestamp(),
672
607
  sint64_node_id,
673
608
  NodeStatus.UNREGISTERED,
674
609
  owner_aid,
@@ -1250,18 +1185,6 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
1250
1185
  return convert_sint64_to_uint64(rows[0]["run_id"])
1251
1186
 
1252
1187
 
1253
- def dict_factory(
1254
- cursor: sqlite3.Cursor,
1255
- row: sqlite3.Row,
1256
- ) -> dict[str, Any]:
1257
- """Turn SQLite results into dicts.
1258
-
1259
- Less efficent for retrival of large amounts of data but easier to use.
1260
- """
1261
- fields = [column[0] for column in cursor.description]
1262
- return dict(zip(fields, row))
1263
-
1264
-
1265
1188
  def message_to_dict(message: Message) -> dict[str, Any]:
1266
1189
  """Transform Message to dict."""
1267
1190
  result = {
@@ -0,0 +1,188 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Mixin providing common SQLite connection and initialization logic."""
16
+
17
+
18
+ import contextlib
19
+ import re
20
+ import sqlite3
21
+ from abc import ABC, abstractmethod
22
+ from collections.abc import Iterator, Sequence
23
+ from logging import DEBUG, ERROR
24
+ from typing import Any, Optional, Union
25
+
26
+ from flwr.common.logger import log
27
+
28
+ DictOrTuple = Union[tuple[Any, ...], dict[str, Any]]
29
+
30
+
31
+ class SqliteMixin(ABC):
32
+ """Mixin providing common SQLite connection and initialization logic."""
33
+
34
+ def __init__(self, database_path: str) -> None:
35
+ self.database_path = database_path
36
+ self._conn: Optional[sqlite3.Connection] = None
37
+
38
+ @property
39
+ def conn(self) -> sqlite3.Connection:
40
+ """Get the SQLite connection."""
41
+ if self._conn is None:
42
+ raise AttributeError("Database not initialized. Call initialize() first.")
43
+ return self._conn
44
+
45
+ @contextlib.contextmanager
46
+ def transaction(self) -> Iterator[None]:
47
+ """Context manager for a transaction.
48
+
49
+ This allows nesting of transactions by checking if a transaction is
50
+ already in progress.
51
+
52
+ Examples
53
+ --------
54
+ ::
55
+
56
+ with self.transaction():
57
+ # Do some DB operations here
58
+ ...
59
+ with self.transaction():
60
+ # Do some more DB operations here
61
+ ...
62
+ """
63
+ if self._conn is None:
64
+ raise AttributeError("Database not initialized. Call initialize() first.")
65
+
66
+ # Start a transaction if not already in one
67
+ if not self._conn.in_transaction:
68
+ self._conn.execute("BEGIN")
69
+ try:
70
+ yield
71
+ self._conn.commit()
72
+ except Exception:
73
+ self._conn.rollback()
74
+ raise
75
+ # Do nothing if already in a transaction
76
+ else:
77
+ yield
78
+
79
+ @abstractmethod
80
+ def initialize(self, log_queries: bool = False) -> list[tuple[str]]:
81
+ """Connect to the DB, enable FK support, and create tables if needed.
82
+
83
+ Parameters
84
+ ----------
85
+ log_queries : bool
86
+ Log each query which is executed.
87
+
88
+ Returns
89
+ -------
90
+ list[tuple[str]]
91
+ The list of all tables in the DB.
92
+
93
+ Examples
94
+ --------
95
+ Implement in subclass:
96
+
97
+ .. code:: python
98
+
99
+ def initialize(self, log_queries: bool = False) -> list[tuple[str]]:
100
+ return self._ensure_initialized(
101
+ SQL_CREATE_TABLE_FOO,
102
+ SQL_CREATE_TABLE_BAR,
103
+ log_queries=log_queries
104
+ )
105
+ """
106
+
107
+ def _ensure_initialized(
108
+ self,
109
+ *create_statements: str,
110
+ log_queries: bool = False,
111
+ ) -> list[tuple[str]]:
112
+ """Connect to the DB, enable FK support, and create tables if needed.
113
+
114
+ Subclasses should call this with their own CREATE TABLE/INDEX statements in
115
+ their `.initialize()` methods.
116
+
117
+ Parameters
118
+ ----------
119
+ create_statements : str
120
+ SQL statements to create tables and indexes.
121
+ log_queries : bool
122
+ Log each query which is executed.
123
+
124
+ Returns
125
+ -------
126
+ list[tuple[str]]
127
+ The list of all tables in the DB.
128
+ """
129
+ self._conn = sqlite3.connect(self.database_path)
130
+ self._conn.execute("PRAGMA foreign_keys = ON;")
131
+ self._conn.row_factory = dict_factory
132
+
133
+ if log_queries:
134
+ self._conn.set_trace_callback(lambda q: log(DEBUG, q))
135
+
136
+ # Create tables and indexes
137
+ cur = self._conn.cursor()
138
+ for sql in create_statements:
139
+ cur.execute(sql)
140
+ res = cur.execute("SELECT name FROM sqlite_schema;")
141
+ return res.fetchall()
142
+
143
+ def query(
144
+ self,
145
+ query: str,
146
+ data: Optional[Union[Sequence[DictOrTuple], DictOrTuple]] = None,
147
+ ) -> list[dict[str, Any]]:
148
+ """Execute a SQL query and return the results as list of dicts."""
149
+ if self._conn is None:
150
+ raise AttributeError("LinkState is not initialized.")
151
+
152
+ if data is None:
153
+ data = []
154
+
155
+ # Clean up whitespace to make the logs nicer
156
+ query = re.sub(r"\s+", " ", query)
157
+
158
+ try:
159
+ with self.transaction():
160
+ if (
161
+ len(data) > 0
162
+ and isinstance(data, (tuple, list))
163
+ and isinstance(data[0], (tuple, dict))
164
+ ):
165
+ rows = self._conn.executemany(query, data)
166
+ else:
167
+ rows = self._conn.execute(query, data)
168
+
169
+ # Extract results before committing to support
170
+ # INSERT/UPDATE ... RETURNING
171
+ # style queries
172
+ result = rows.fetchall()
173
+ except KeyError as exc:
174
+ log(ERROR, {"query": query, "data": data, "exception": exc})
175
+
176
+ return result
177
+
178
+
179
+ def dict_factory(
180
+ cursor: sqlite3.Cursor,
181
+ row: sqlite3.Row,
182
+ ) -> dict[str, Any]:
183
+ """Turn SQLite results into dicts.
184
+
185
+ Less efficent for retrival of large amounts of data but easier to use.
186
+ """
187
+ fields = [column[0] for column in cursor.description]
188
+ return dict(zip(fields, row))
@@ -18,7 +18,6 @@
18
18
  import hashlib
19
19
  import time
20
20
  from collections.abc import Generator, Sequence
21
- from datetime import timedelta
22
21
  from logging import ERROR, INFO
23
22
  from typing import Any, Optional, cast
24
23
 
@@ -72,7 +71,6 @@ from flwr.proto.control_pb2 import ( # pylint: disable=E0611
72
71
  )
73
72
  from flwr.proto.node_pb2 import NodeInfo # pylint: disable=E0611
74
73
  from flwr.server.superlink.linkstate import LinkState, LinkStateFactory
75
- from flwr.supercore.constant import NodeStatus
76
74
  from flwr.supercore.ffs import FfsFactory
77
75
  from flwr.supercore.object_store import ObjectStore, ObjectStoreFactory
78
76
  from flwr.supercore.primitives.asymmetric import bytes_to_public_key, uses_nist_ec_curve
@@ -466,79 +464,17 @@ class ControlServicer(control_pb2_grpc.ControlServicer):
466
464
  raise grpc.RpcError() # This line is unreachable
467
465
 
468
466
  nodes_info: Sequence[NodeInfo] = []
469
- # If dry run is enabled, create dummy NodeInfo data
470
- if request.dry_run:
471
- nodes_info = _create_list_nodeif_for_dry_run()
472
-
473
- else:
474
- # Init link state
475
- state = self.linkstate_factory.state()
467
+ # Init link state
468
+ state = self.linkstate_factory.state()
476
469
 
477
- flwr_aid = shared_account_info.get().flwr_aid
478
- flwr_aid = _check_flwr_aid_exists(flwr_aid, context)
479
- # Retrieve all nodes for the account
480
- nodes_info = state.get_node_info(owner_aids=[flwr_aid])
470
+ flwr_aid = shared_account_info.get().flwr_aid
471
+ flwr_aid = _check_flwr_aid_exists(flwr_aid, context)
472
+ # Retrieve all nodes for the account
473
+ nodes_info = state.get_node_info(owner_aids=[flwr_aid])
481
474
 
482
475
  return ListNodesResponse(nodes_info=nodes_info, now=now().isoformat())
483
476
 
484
477
 
485
- def _create_list_nodeif_for_dry_run() -> Sequence[NodeInfo]:
486
- """Create a list of NodeInfo for dry run testing."""
487
- nodes_info: list[NodeInfo] = []
488
- # A node registered (but not connected)
489
- nodes_info.append(
490
- NodeInfo(
491
- node_id=15390646978706312628,
492
- owner_aid="owner_aid_1",
493
- status=NodeStatus.REGISTERED,
494
- registered_at=(now()).isoformat(),
495
- last_activated_at="",
496
- last_deactivated_at="",
497
- unregistered_at="",
498
- )
499
- )
500
-
501
- # A node registered and connected
502
- nodes_info.append(
503
- NodeInfo(
504
- node_id=2941141058168602545,
505
- owner_aid="owner_aid_2",
506
- status=NodeStatus.ONLINE,
507
- registered_at=(now()).isoformat(),
508
- last_activated_at=(now() + timedelta(hours=0.5)).isoformat(),
509
- last_deactivated_at="",
510
- unregistered_at="",
511
- )
512
- )
513
-
514
- # A node registered and unregistered (never connected)
515
- nodes_info.append(
516
- NodeInfo(
517
- node_id=906971720890549292,
518
- owner_aid="owner_aid_3",
519
- status=NodeStatus.UNREGISTERED,
520
- registered_at=(now()).isoformat(),
521
- last_activated_at="",
522
- last_deactivated_at="",
523
- unregistered_at=(now() + timedelta(hours=1)).isoformat(),
524
- )
525
- )
526
-
527
- # A node registered, deactivate and then unregistered
528
- nodes_info.append(
529
- NodeInfo(
530
- node_id=1781174086018058152,
531
- owner_aid="owner_aid_4",
532
- status=NodeStatus.OFFLINE,
533
- registered_at=(now()).isoformat(),
534
- last_activated_at=(now() + timedelta(hours=0.5)).isoformat(),
535
- last_deactivated_at=(now() + timedelta(hours=1)).isoformat(),
536
- unregistered_at=(now() + timedelta(hours=1.5)).isoformat(),
537
- )
538
- )
539
- return nodes_info
540
-
541
-
542
478
  def _create_list_runs_response(
543
479
  run_ids: set[int], state: LinkState, store: ObjectStore
544
480
  ) -> ListRunsResponse:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: flwr-nightly
3
- Version: 1.23.0.dev20251020
3
+ Version: 1.23.0.dev20251021
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  License: Apache-2.0
6
6
  Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
@@ -18,7 +18,7 @@ flwr/cli/install.py,sha256=Jr883qR7qssVpUr3hEOEcLK-dfW67Rsve3lZchjA9RU,8180
18
18
  flwr/cli/log.py,sha256=3qVYY30QqchfnKRTeeRw2ojBUj_nwvfnZkdBr4lmjpc,6541
19
19
  flwr/cli/login/__init__.py,sha256=B1SXKU3HCQhWfFDMJhlC7FOl8UsvH4mxysxeBnrfyUE,800
20
20
  flwr/cli/login/login.py,sha256=XeA6K9rZn3CBvtiFP3XWTAk7s8766BCqHvCsO_tJEuY,4558
21
- flwr/cli/ls.py,sha256=VHF9xb4q0dOQwFZE_XmUhX_cw27eKyUvlIyxNx-crkw,11095
21
+ flwr/cli/ls.py,sha256=MUlWSjO0yhVTupN5Fr0zvdvRqfRBGWliuoyWA0D-Pzg,11107
22
22
  flwr/cli/new/__init__.py,sha256=QA1E2QtzPvFCjLTUHnFnJbufuFiGyT_0Y53Wpbvg1F0,790
23
23
  flwr/cli/new/new.py,sha256=nIuUrQSGDjI4kqnymlq-rOT0RU3AHwZrat3abqHhCwM,10598
24
24
  flwr/cli/new/templates/__init__.py,sha256=FpjWCfIySU2DB4kh0HOXLAjlZNNFDTVU4w3HoE2TzcI,725
@@ -87,7 +87,7 @@ flwr/cli/run/__init__.py,sha256=RPyB7KbYTFl6YRiilCch6oezxrLQrl1kijV7BMGkLbA,790
87
87
  flwr/cli/run/run.py,sha256=ED1mDmO1PnSAgtVOrCeWwzwPm6t3aFYSs3Rh36BJzqk,8161
88
88
  flwr/cli/stop.py,sha256=W7ynTYLm0-_1nC5Il4IaZTji6A3GCCm_0R-HQUudAsI,4988
89
89
  flwr/cli/supernode/__init__.py,sha256=DBkjoPo2hS2Y-ghJxwLbrAbCQFBgD82_Itl2_892UBo,917
90
- flwr/cli/supernode/ls.py,sha256=aRqPK2kB0qbmgLysFcDqw5Z7ETK7ABL4CQ9sTLfEh8Q,8813
90
+ flwr/cli/supernode/ls.py,sha256=OVEs9zPSomxxiwCg1Jz8AN5MkZJDf3xAE-lxCG22zyE,8640
91
91
  flwr/cli/supernode/register.py,sha256=8xLsaJWHGrFtqxPTEMkvQdlgWd5hqQZu94b_jpCN_A0,6419
92
92
  flwr/cli/supernode/unregister.py,sha256=qbrAMbzjLhOJNQ_ndSqBLQp1up9LrxBN1ngwg97Vbj4,4578
93
93
  flwr/cli/utils.py,sha256=66RqZdF0DKsqmR4ZqE9zZ1bX6opdxD2U50GV41WQDoY,14864
@@ -126,7 +126,7 @@ flwr/common/__init__.py,sha256=5GCLVk399Az_rTJHNticRlL0Sl_oPw_j5_LuFKfX7-M,4171
126
126
  flwr/common/address.py,sha256=9JucdTwlc-jpeJkRKeUboZoacUtErwSVtnDR9kAtLqE,4119
127
127
  flwr/common/args.py,sha256=Nq2u4yePbkSY0CWFamn0hZY6Rms8G1xYDeDGIcLIITE,5849
128
128
  flwr/common/config.py,sha256=glcZDjco-amw1YfQcYTFJ4S1pt9APoexT-mf1QscuHs,13960
129
- flwr/common/constant.py,sha256=vkHawDmnH0Czt3y8L2FBgj4NHaqal76M6lSEAwE1Z1s,9656
129
+ flwr/common/constant.py,sha256=y9U6LdgMzNYe-LGTjooGn0QJj5MVTlWjbriE1l5rkag,9898
130
130
  flwr/common/context.py,sha256=Be8obQR_OvEDy1OmshuUKxGRQ7Qx89mf5F4xlhkR10s,2407
131
131
  flwr/common/date.py,sha256=1ZT2cRSpC2DJqprOVTLXYCR_O2_OZR0zXO_brJ3LqWc,1554
132
132
  flwr/common/differential_privacy.py,sha256=FdlpdpPl_H_2HJa8CQM1iCUGBBQ5Dc8CzxmHERM-EoE,6148
@@ -143,14 +143,14 @@ flwr/common/grpc.py,sha256=nHnFC7E84pZVTvd6BhcSYWnGd0jf8t5UmGea04qvilM,9806
143
143
  flwr/common/heartbeat.py,sha256=SyEpNDnmJ0lni0cWO67rcoJVKasCLmkNHm3dKLeNrLU,5749
144
144
  flwr/common/inflatable.py,sha256=GDL9oBKs16_yyVdlH6kBf493O5xll_h9V7XB5Mpx1Hc,9524
145
145
  flwr/common/inflatable_protobuf_utils.py,sha256=JtRqp-fV47goDM2y8JRQ7AmwwjeGaWexwoMWLcxX5gE,5056
146
- flwr/common/inflatable_utils.py,sha256=njQB7u4OL4AMraRgx23L84JEz5upeXMuYWaPZJ_T-aM,18946
146
+ flwr/common/inflatable_utils.py,sha256=1ibsNLvjbXtRSWu9pjBbL__F6N3vyUM4dgJKperlwYs,19116
147
147
  flwr/common/logger.py,sha256=kP7Cbs2WuYFK83Wsx5o9qc9mj8jDSyUK3BfRjvxhSTQ,13049
148
148
  flwr/common/message.py,sha256=xAL7iZN5-n-xPQpgoSFvxNrzs8fmiiPfoU0DjNQEhRw,19953
149
149
  flwr/common/object_ref.py,sha256=p3SfTeqo3Aj16SkB-vsnNn01zswOPdGNBitcbRnqmUk,9134
150
150
  flwr/common/parameter.py,sha256=UVw6sOgehEFhFs4uUCMl2kfVq1PD6ncmWgPLMsZPKPE,2095
151
151
  flwr/common/pyproject.py,sha256=2SU6yJW7059SbMXgzjOdK1GZRWO6AixDH7BmdxbMvHI,1386
152
152
  flwr/common/record/__init__.py,sha256=cNGccdDoxttqgnUgyKRIqLWULjW-NaSmOufVxtXq-sw,1197
153
- flwr/common/record/array.py,sha256=6ybCfUZ13zI_CZKETUAVhIwlmbEGj4k6JazvVX5rOPw,15038
153
+ flwr/common/record/array.py,sha256=wI-2UmUXelxWAQJLV0n5I_G1453wpCebi5fn95Kn3FI,15077
154
154
  flwr/common/record/arraychunk.py,sha256=gU5h6uP7H_06Z14wNixtwH2FvepQkl9FpALXg9qcbhM,1936
155
155
  flwr/common/record/arrayrecord.py,sha256=HpNM9NmOoGhjuo5CfxrrjL3uJevpXZLkyq_wPhJW1No,18278
156
156
  flwr/common/record/configrecord.py,sha256=G7U0q39kB0Kyi0zMxFmPxcVemL9NgwVS1qjvI4BRQuU,9763
@@ -191,8 +191,8 @@ flwr/proto/clientappio_pb2.py,sha256=vJjzwWydhg7LruK8cvRAeVQeHPsJztgdIW9nyiPBZF0
191
191
  flwr/proto/clientappio_pb2.pyi,sha256=XbFvpZvvrS7QcH5AFXfpRGl4hQvhd3QdKO6x0oTlCCU,165
192
192
  flwr/proto/clientappio_pb2_grpc.py,sha256=iobNROP0qvn5zddx7k-uIi_dJWP3T_BRp_kbKq086i8,17550
193
193
  flwr/proto/clientappio_pb2_grpc.pyi,sha256=Ytf1O1ktKB0Vsuc3AWLIErGjIJYokzKYzi2uA7mdMeg,4785
194
- flwr/proto/control_pb2.py,sha256=DCG9jT1Z7ipDyBSHIaPl7QJ-oJn4mCcHYvJuZlbPyqA,7746
195
- flwr/proto/control_pb2.pyi,sha256=V_cGkSTQT3ELpWNyH02_JBhN_FYjjUEP_2QAV-JtU9E,13582
194
+ flwr/proto/control_pb2.py,sha256=_FqVaMs-LP6eYJ3eDARnbfsq_q66sewTclDskBMOb3c,7707
195
+ flwr/proto/control_pb2.pyi,sha256=0vlxyu7uYVtIhqFcjHCDeWPdtuQCCzBRyAEU5VYeaxI,13368
196
196
  flwr/proto/control_pb2_grpc.py,sha256=wLjMi8GNQ5VR7IbNdDbHC0A1QMzA_0CegBzaOO1dlo0,17152
197
197
  flwr/proto/control_pb2_grpc.pyi,sha256=L1HGnIXWu0Q7WQ1ONWffaQ69jHtWQ6cyEUcxINL8_Uc,4801
198
198
  flwr/proto/error_pb2.py,sha256=PQVWrfjVPo88ql_KgV9nCxyQNCcV9PVfmcw7sOzTMro,1084
@@ -317,10 +317,10 @@ flwr/server/superlink/fleet/vce/backend/backend.py,sha256=cSrHZ5SjCCvy4vI0pgsyjt
317
317
  flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=cBZYTmfiAsb1HmVUmOQXYLU-UJmJTFWkj1wW4RYRDuc,7218
318
318
  flwr/server/superlink/fleet/vce/vce_api.py,sha256=EU0DLt4njtKelOpOWfQ7zWW45bSVC6K7pPYfHSyOJwM,13332
319
319
  flwr/server/superlink/linkstate/__init__.py,sha256=OtsgvDTnZLU3k0sUbkHbqoVwW6ql2FDmb6uT6DbNkZo,1064
320
- flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=ARlySiNPtniXhfZOK90pt2ETYQBhs8bKFfh6i0lrncM,29725
320
+ flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=9JBJSWh7TAEf7WFcpdn03rL0tLrroVUPZcyNJd8Qz88,29997
321
321
  flwr/server/superlink/linkstate/linkstate.py,sha256=JKgVEPnH2T-nixW3LHp8jR3g4ITAZYNwEoDIWZaUYmQ,14701
322
322
  flwr/server/superlink/linkstate/linkstate_factory.py,sha256=8RlosqSpKOoD_vhUUQPY0jtE3A84GeF96Z7sWNkRRcA,2069
323
- flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=RJDSasa4wYPOOj8HXtFSlelTJmGeukifF8Er-qrTvYY,47909
323
+ flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=zpLonYzlE1aOeCRGTwHJ2UBTtz-JZ-6-YgCPKjqUUjM,45585
324
324
  flwr/server/superlink/linkstate/utils.py,sha256=IeLh7iGRCHU5MEWOl7iriaSE4L__8GWOa2OleXadK5M,15444
325
325
  flwr/server/superlink/serverappio/__init__.py,sha256=Fy4zJuoccZe5mZSEIpOmQvU6YeXFBa1M4eZuXXmJcn8,717
326
326
  flwr/server/superlink/serverappio/serverappio_grpc.py,sha256=-I7kBbr4w4ZVYwBZoAIle-xHKthFnZrsVfxa6WR8uxA,2310
@@ -394,6 +394,7 @@ flwr/supercore/object_store/object_store_factory.py,sha256=QVwE2ywi7vsj2iKfvWWnN
394
394
  flwr/supercore/object_store/utils.py,sha256=DcPbrb9PenloAPoQRiKiXX9DrDfpXcSyY0cZpgn4PeQ,1680
395
395
  flwr/supercore/primitives/__init__.py,sha256=Tx8GOjnmMo8Y74RsDGrMpfr-E0Nl8dcUDF784_ge6F8,745
396
396
  flwr/supercore/primitives/asymmetric.py,sha256=wpO0o0G_vStRknFitw2SqyIBSzaBfuXfMc44u-UcxTs,3774
397
+ flwr/supercore/sqlite_mixin.py,sha256=uQ9LK_D2o8dYVEXzAtoJIeQya2zaUJF1ifSN06dwb1E,5934
397
398
  flwr/supercore/superexec/__init__.py,sha256=XKX208hZ6a9gZ4KT9kMqfpCtp_8VGxekzKFfHSu2esQ,707
398
399
  flwr/supercore/superexec/plugin/__init__.py,sha256=GNwq8uNdE8RB7ywEFRAvKjLFzgS3YXgz39-HBGsemWw,1035
399
400
  flwr/supercore/superexec/plugin/base_exec_plugin.py,sha256=fL-Ufc9Dp56OhWOzNSJUc7HumbkuSDYqZKwde2opG4g,2074
@@ -415,7 +416,7 @@ flwr/superlink/servicer/control/control_account_auth_interceptor.py,sha256=Tbi4W
415
416
  flwr/superlink/servicer/control/control_event_log_interceptor.py,sha256=5uBl6VcJlUOgCF0d4kmsmJc1Rs1qxyouaZv0-uH2axs,5969
416
417
  flwr/superlink/servicer/control/control_grpc.py,sha256=MRCaX4I2a5ogjKmhtFs6Mj-VdWemxL2h3gU9QbQmvCA,4183
417
418
  flwr/superlink/servicer/control/control_license_interceptor.py,sha256=T3AzmRt-PPwyTq3hrdpmZHQd5_CpPOk7TtnFZrB-JRY,3349
418
- flwr/superlink/servicer/control/control_servicer.py,sha256=CgnjtkfkxKDr309TeYBQtOQhz5-MBE74pC_M6pKFAoE,21496
419
+ flwr/superlink/servicer/control/control_servicer.py,sha256=52SHq8yPCDjO92jFm9bg_QMjZJ_-kDC0brn7uJAOwnw,19357
419
420
  flwr/supernode/__init__.py,sha256=KgeCaVvXWrU3rptNR1y0oBp4YtXbAcrnCcJAiOoWkI4,707
420
421
  flwr/supernode/cli/__init__.py,sha256=JuEMr0-s9zv-PEWKuLB9tj1ocNfroSyNJ-oyv7ati9A,887
421
422
  flwr/supernode/cli/flower_supernode.py,sha256=bmPpg88Zq7NYMDzyyDwBzeZ6_1f26fD_iDdGw1o_4QQ,8334
@@ -430,7 +431,7 @@ flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca
430
431
  flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
431
432
  flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=nIHRu38EWK-rpNOkcgBRAAKwYQQWFeCwu0lkO7OPZGQ,10239
432
433
  flwr/supernode/start_client_internal.py,sha256=wKqh9-_rQYi7JFKpYBLRiUeq9YJUSyUd9b-SnVnuvwI,21567
433
- flwr_nightly-1.23.0.dev20251020.dist-info/METADATA,sha256=oFA3OFqIkiXRU6zhT4JhuHq4Lwcgcwg8X0DfvKPYDds,14559
434
- flwr_nightly-1.23.0.dev20251020.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
435
- flwr_nightly-1.23.0.dev20251020.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
436
- flwr_nightly-1.23.0.dev20251020.dist-info/RECORD,,
434
+ flwr_nightly-1.23.0.dev20251021.dist-info/METADATA,sha256=RKCjEZQAhaeqPaGtLHA3n0zhcMH1GBgkT8hXrxqwhSg,14559
435
+ flwr_nightly-1.23.0.dev20251021.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
436
+ flwr_nightly-1.23.0.dev20251021.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
437
+ flwr_nightly-1.23.0.dev20251021.dist-info/RECORD,,