flwr-nightly 1.23.0.dev20251004__py3-none-any.whl → 1.23.0.dev20251006__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.
flwr/cli/supernode/ls.py CHANGED
@@ -15,10 +15,16 @@
15
15
  """Flower command line interface `supernode list` command."""
16
16
 
17
17
 
18
+ import io
19
+ import json
20
+ from datetime import datetime, timedelta
18
21
  from pathlib import Path
19
- from typing import Annotated, Optional
22
+ from typing import Annotated, Optional, cast
20
23
 
21
24
  import typer
25
+ from rich.console import Console
26
+ from rich.table import Table
27
+ from rich.text import Text
22
28
 
23
29
  from flwr.cli.config_utils import (
24
30
  exit_if_no_address,
@@ -26,10 +32,23 @@ from flwr.cli.config_utils import (
26
32
  process_loaded_project_config,
27
33
  validate_federation_in_project_config,
28
34
  )
29
- from flwr.common.constant import FAB_CONFIG_FILE
35
+ from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat
36
+ from flwr.common.date import format_timedelta, isoformat8601_utc
37
+ from flwr.common.logger import print_json_error, redirect_output, restore_output
38
+ from flwr.proto.control_pb2 import ( # pylint: disable=E0611
39
+ ListNodesCliRequest,
40
+ ListNodesCliResponse,
41
+ )
42
+ from flwr.proto.control_pb2_grpc import ControlStub
43
+ from flwr.proto.node_pb2 import NodeInfo # pylint: disable=E0611
44
+
45
+ from ..utils import flwr_cli_grpc_exc_handler, init_channel, try_obtain_cli_auth_plugin
46
+
47
+ _NodeListType = tuple[int, str, str, str, str, str, str, str]
30
48
 
31
49
 
32
50
  def ls( # pylint: disable=R0914
51
+ ctx: typer.Context,
33
52
  app: Annotated[
34
53
  Path,
35
54
  typer.Argument(help="Path of the Flower project"),
@@ -38,14 +57,202 @@ def ls( # pylint: disable=R0914
38
57
  Optional[str],
39
58
  typer.Argument(help="Name of the federation"),
40
59
  ] = None,
60
+ output_format: Annotated[
61
+ str,
62
+ typer.Option(
63
+ "--format",
64
+ case_sensitive=False,
65
+ help="Format output using 'default' view or 'json'",
66
+ ),
67
+ ] = CliOutputFormat.DEFAULT,
68
+ verbose: Annotated[
69
+ bool,
70
+ typer.Option(
71
+ "--verbose",
72
+ "-v",
73
+ help="Enable verbose output",
74
+ ),
75
+ ] = False,
41
76
  ) -> None:
42
77
  """List SuperNodes in the federation."""
43
- typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
78
+ # Resolve command used (list or ls)
79
+ command_name = cast(str, ctx.command.name) if ctx.command else "ls"
80
+
81
+ suppress_output = output_format == CliOutputFormat.JSON
82
+ captured_output = io.StringIO()
83
+ try:
84
+ if suppress_output:
85
+ redirect_output(captured_output)
86
+ typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
87
+
88
+ pyproject_path = app / FAB_CONFIG_FILE if app else None
89
+ config, errors, warnings = load_and_validate(path=pyproject_path)
90
+ config = process_loaded_project_config(config, errors, warnings)
91
+ federation, federation_config = validate_federation_in_project_config(
92
+ federation, config
93
+ )
94
+ exit_if_no_address(federation_config, f"supernode {command_name}")
95
+ channel = None
96
+ try:
97
+ auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
98
+ channel = init_channel(app, federation_config, auth_plugin)
99
+ stub = ControlStub(channel)
100
+ typer.echo("📄 Listing all nodes...")
101
+ formatted_nodes = _list_nodes(stub)
102
+ restore_output()
103
+ if output_format == CliOutputFormat.JSON:
104
+ Console().print_json(_to_json(formatted_nodes, verbose=verbose))
105
+ else:
106
+ Console().print(_to_table(formatted_nodes, verbose=verbose))
107
+
108
+ finally:
109
+ if channel:
110
+ channel.close()
111
+ except (typer.Exit, Exception) as err: # pylint: disable=broad-except
112
+ if suppress_output:
113
+ restore_output()
114
+ e_message = captured_output.getvalue()
115
+ print_json_error(e_message, err)
116
+ else:
117
+ typer.secho(
118
+ f"{err}",
119
+ fg=typer.colors.RED,
120
+ bold=True,
121
+ )
122
+ finally:
123
+ if suppress_output:
124
+ restore_output()
125
+ captured_output.close()
126
+
127
+
128
+ def _list_nodes(stub: ControlStub) -> list[_NodeListType]:
129
+ """List all nodes."""
130
+ with flwr_cli_grpc_exc_handler():
131
+ res: ListNodesCliResponse = stub.ListNodesCli(ListNodesCliRequest())
132
+
133
+ return _format_nodes(list(res.nodes_info), res.now)
134
+
135
+
136
+ def _format_nodes(
137
+ nodes_info: list[NodeInfo], now_isoformat: str
138
+ ) -> list[_NodeListType]:
139
+ """Format node information for display."""
140
+
141
+ def _format_datetime(dt_str: Optional[str]) -> str:
142
+ dt = datetime.fromisoformat(dt_str) if dt_str else None
143
+ return isoformat8601_utc(dt).replace("T", " ") if dt else "N/A"
144
+
145
+ formatted_nodes: list[_NodeListType] = []
146
+ # Add rows
147
+ for node in sorted(nodes_info, key=lambda x: datetime.fromisoformat(x.created_at)):
148
+
149
+ # Calculate elapsed times
150
+ elapsed_time_activated = timedelta()
151
+ if node.last_activated_at:
152
+ end_time = datetime.fromisoformat(now_isoformat)
153
+ elapsed_time_activated = end_time - datetime.fromisoformat(
154
+ node.last_activated_at
155
+ )
44
156
 
45
- pyproject_path = app / FAB_CONFIG_FILE if app else None
46
- config, errors, warnings = load_and_validate(path=pyproject_path)
47
- config = process_loaded_project_config(config, errors, warnings)
48
- federation, federation_config = validate_federation_in_project_config(
49
- federation, config
157
+ formatted_nodes.append(
158
+ (
159
+ node.node_id,
160
+ node.owner_aid,
161
+ node.status,
162
+ _format_datetime(node.created_at),
163
+ _format_datetime(node.last_activated_at),
164
+ _format_datetime(node.last_deactivated_at),
165
+ _format_datetime(node.deleted_at),
166
+ format_timedelta(elapsed_time_activated),
167
+ )
168
+ )
169
+
170
+ return formatted_nodes
171
+
172
+
173
+ def _to_table(nodes_info: list[_NodeListType], verbose: bool) -> Table:
174
+ """Format the provided node list to a rich Table."""
175
+ table = Table(header_style="bold cyan", show_lines=True)
176
+
177
+ # Add columns
178
+ table.add_column(
179
+ Text("Node ID", justify="center"), style="bright_white", no_wrap=True
50
180
  )
51
- exit_if_no_address(federation_config, "supernode ls")
181
+ table.add_column(Text("Owner", justify="center"), style="dim white")
182
+ table.add_column(Text("Status", justify="center"))
183
+ table.add_column(Text("Elapsed", justify="center"))
184
+ table.add_column(Text("Status Changed @", justify="center"), style="dim white")
185
+
186
+ for row in nodes_info:
187
+ (
188
+ node_id,
189
+ owner_aid,
190
+ status,
191
+ _,
192
+ last_activated_at,
193
+ last_deactivated_at,
194
+ deleted_at,
195
+ elapse_activated,
196
+ ) = row
197
+
198
+ if status == "online":
199
+ status_style = "green"
200
+ time_at = last_activated_at
201
+ elif status == "offline":
202
+ status_style = "bright_yellow"
203
+ time_at = last_deactivated_at
204
+ elif status == "deleted":
205
+ if not verbose:
206
+ continue
207
+ status_style = "red"
208
+ time_at = deleted_at
209
+ elif status == "created":
210
+ status_style = "blue"
211
+ time_at = "N/A"
212
+ else:
213
+ raise ValueError(f"Unexpected node status '{status}'")
214
+
215
+ formatted_row = (
216
+ f"[bold]{node_id}[/bold]",
217
+ f"{owner_aid}",
218
+ f"[{status_style}]{status}",
219
+ f"[cyan]{elapse_activated}[/cyan]" if status == "online" else "",
220
+ time_at,
221
+ )
222
+ table.add_row(*formatted_row)
223
+
224
+ return table
225
+
226
+
227
+ def _to_json(nodes_info: list[_NodeListType], verbose: bool) -> str:
228
+ """Format node list to a JSON formatted string."""
229
+ nodes_list = []
230
+ for row in nodes_info:
231
+ (
232
+ node_id,
233
+ owner_aid,
234
+ status,
235
+ created_at,
236
+ activated_at,
237
+ deactivated_at,
238
+ deleted_at,
239
+ elapse_activated,
240
+ ) = row
241
+
242
+ if status == "deleted" and not verbose:
243
+ continue
244
+
245
+ nodes_list.append(
246
+ {
247
+ "node-id": node_id,
248
+ "owner-aid": owner_aid,
249
+ "status": status,
250
+ "created-at": created_at,
251
+ "online-at": activated_at,
252
+ "online-elapsed": elapse_activated,
253
+ "offline-at": deactivated_at,
254
+ "deleted-at": deleted_at,
255
+ }
256
+ )
257
+
258
+ return json.dumps({"success": True, "nodes": nodes_list})
flwr/proto/node_pb2.py CHANGED
@@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default()
14
14
 
15
15
 
16
16
 
17
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/node.proto\x12\nflwr.proto\"\x17\n\x04Node\x12\x0f\n\x07node_id\x18\x01 \x01(\x04\"\xb6\x01\n\x08NodeInfo\x12\x0f\n\x07node_id\x18\x01 \x01(\x04\x12\x11\n\towner_aid\x18\x02 \x01(\t\x12\x12\n\ncreated_at\x18\x03 \x01(\t\x12\x14\n\x0c\x61\x63tivated_at\x18\x04 \x01(\t\x12\x16\n\x0e\x64\x65\x61\x63tivated_at\x18\x05 \x01(\t\x12\x12\n\ndeleted_at\x18\x06 \x01(\t\x12\x14\n\x0conline_until\x18\x07 \x01(\x02\x12\x1a\n\x12heartbeat_interval\x18\x08 \x01(\x02\x62\x06proto3')
17
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/node.proto\x12\nflwr.proto\"\x17\n\x04Node\x12\x0f\n\x07node_id\x18\x01 \x01(\x04\"\xd0\x01\n\x08NodeInfo\x12\x0f\n\x07node_id\x18\x01 \x01(\x04\x12\x11\n\towner_aid\x18\x02 \x01(\t\x12\x0e\n\x06status\x18\x03 \x01(\t\x12\x12\n\ncreated_at\x18\x04 \x01(\t\x12\x19\n\x11last_activated_at\x18\x05 \x01(\t\x12\x1b\n\x13last_deactivated_at\x18\x06 \x01(\t\x12\x12\n\ndeleted_at\x18\x07 \x01(\t\x12\x14\n\x0conline_until\x18\x08 \x01(\x02\x12\x1a\n\x12heartbeat_interval\x18\t \x01(\x02\x62\x06proto3')
18
18
 
19
19
  _globals = globals()
20
20
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -24,5 +24,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
24
24
  _globals['_NODE']._serialized_start=37
25
25
  _globals['_NODE']._serialized_end=60
26
26
  _globals['_NODEINFO']._serialized_start=63
27
- _globals['_NODEINFO']._serialized_end=245
27
+ _globals['_NODEINFO']._serialized_end=271
28
28
  # @@protoc_insertion_point(module_scope)
flwr/proto/node_pb2.pyi CHANGED
@@ -25,17 +25,19 @@ class NodeInfo(google.protobuf.message.Message):
25
25
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
26
26
  NODE_ID_FIELD_NUMBER: builtins.int
27
27
  OWNER_AID_FIELD_NUMBER: builtins.int
28
+ STATUS_FIELD_NUMBER: builtins.int
28
29
  CREATED_AT_FIELD_NUMBER: builtins.int
29
- ACTIVATED_AT_FIELD_NUMBER: builtins.int
30
- DEACTIVATED_AT_FIELD_NUMBER: builtins.int
30
+ LAST_ACTIVATED_AT_FIELD_NUMBER: builtins.int
31
+ LAST_DEACTIVATED_AT_FIELD_NUMBER: builtins.int
31
32
  DELETED_AT_FIELD_NUMBER: builtins.int
32
33
  ONLINE_UNTIL_FIELD_NUMBER: builtins.int
33
34
  HEARTBEAT_INTERVAL_FIELD_NUMBER: builtins.int
34
35
  node_id: builtins.int
35
36
  owner_aid: typing.Text
37
+ status: typing.Text
36
38
  created_at: typing.Text
37
- activated_at: typing.Text
38
- deactivated_at: typing.Text
39
+ last_activated_at: typing.Text
40
+ last_deactivated_at: typing.Text
39
41
  deleted_at: typing.Text
40
42
  online_until: builtins.float
41
43
  heartbeat_interval: builtins.float
@@ -43,12 +45,13 @@ class NodeInfo(google.protobuf.message.Message):
43
45
  *,
44
46
  node_id: builtins.int = ...,
45
47
  owner_aid: typing.Text = ...,
48
+ status: typing.Text = ...,
46
49
  created_at: typing.Text = ...,
47
- activated_at: typing.Text = ...,
48
- deactivated_at: typing.Text = ...,
50
+ last_activated_at: typing.Text = ...,
51
+ last_deactivated_at: typing.Text = ...,
49
52
  deleted_at: typing.Text = ...,
50
53
  online_until: builtins.float = ...,
51
54
  heartbeat_interval: builtins.float = ...,
52
55
  ) -> None: ...
53
- def ClearField(self, field_name: typing_extensions.Literal["activated_at",b"activated_at","created_at",b"created_at","deactivated_at",b"deactivated_at","deleted_at",b"deleted_at","heartbeat_interval",b"heartbeat_interval","node_id",b"node_id","online_until",b"online_until","owner_aid",b"owner_aid"]) -> None: ...
56
+ def ClearField(self, field_name: typing_extensions.Literal["created_at",b"created_at","deleted_at",b"deleted_at","heartbeat_interval",b"heartbeat_interval","last_activated_at",b"last_activated_at","last_deactivated_at",b"last_deactivated_at","node_id",b"node_id","online_until",b"online_until","owner_aid",b"owner_aid","status",b"status"]) -> None: ...
54
57
  global___NodeInfo = NodeInfo
@@ -18,6 +18,7 @@
18
18
  import hashlib
19
19
  import time
20
20
  from collections.abc import Generator
21
+ from datetime import timedelta
21
22
  from logging import ERROR, INFO
22
23
  from typing import Any, Optional, cast
23
24
 
@@ -65,6 +66,7 @@ from flwr.proto.control_pb2 import ( # pylint: disable=E0611
65
66
  StreamLogsRequest,
66
67
  StreamLogsResponse,
67
68
  )
69
+ from flwr.proto.node_pb2 import NodeInfo # pylint: disable=E0611
68
70
  from flwr.server.superlink.linkstate import LinkState, LinkStateFactory
69
71
  from flwr.supercore.ffs import FfsFactory
70
72
  from flwr.supercore.object_store import ObjectStore, ObjectStoreFactory
@@ -418,7 +420,61 @@ class ControlServicer(control_pb2_grpc.ControlServicer):
418
420
  ) -> ListNodesCliResponse:
419
421
  """List all SuperNodes."""
420
422
  log(INFO, "ControlServicer.ListNodesCli")
421
- return ListNodesCliResponse()
423
+
424
+ nodes_info = []
425
+ # A node created (but not connected)
426
+ nodes_info.append(
427
+ NodeInfo(
428
+ node_id=15390646978706312628,
429
+ owner_aid="owner_aid_1",
430
+ status="created",
431
+ created_at=(now()).isoformat(),
432
+ last_activated_at="",
433
+ last_deactivated_at="",
434
+ deleted_at="",
435
+ )
436
+ )
437
+
438
+ # A node created and connected
439
+ nodes_info.append(
440
+ NodeInfo(
441
+ node_id=2941141058168602545,
442
+ owner_aid="owner_aid_2",
443
+ status="online",
444
+ created_at=(now()).isoformat(),
445
+ last_activated_at=(now() + timedelta(hours=0.5)).isoformat(),
446
+ last_deactivated_at="",
447
+ deleted_at="",
448
+ )
449
+ )
450
+
451
+ # A node created and deleted (never connected)
452
+ nodes_info.append(
453
+ NodeInfo(
454
+ node_id=906971720890549292,
455
+ owner_aid="owner_aid_3",
456
+ status="deleted",
457
+ created_at=(now()).isoformat(),
458
+ last_activated_at="",
459
+ last_deactivated_at="",
460
+ deleted_at=(now() + timedelta(hours=1)).isoformat(),
461
+ )
462
+ )
463
+
464
+ # A node created, deactivate and then deleted
465
+ nodes_info.append(
466
+ NodeInfo(
467
+ node_id=1781174086018058152,
468
+ owner_aid="owner_aid_4",
469
+ status="offline",
470
+ created_at=(now()).isoformat(),
471
+ last_activated_at=(now() + timedelta(hours=0.5)).isoformat(),
472
+ last_deactivated_at=(now() + timedelta(hours=1)).isoformat(),
473
+ deleted_at=(now() + timedelta(hours=1.5)).isoformat(),
474
+ )
475
+ )
476
+
477
+ return ListNodesCliResponse(nodes_info=nodes_info, now=now().isoformat())
422
478
 
423
479
 
424
480
  def _create_list_runs_response(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: flwr-nightly
3
- Version: 1.23.0.dev20251004
3
+ Version: 1.23.0.dev20251006
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
@@ -89,7 +89,7 @@ flwr/cli/stop.py,sha256=TR9F61suTxNUzGIktUdoBhXwdRtCdvzGhy3qCuvcfBg,5000
89
89
  flwr/cli/supernode/__init__.py,sha256=DVrTcyCg9NFll6glPLAAA6WPi7boxu6pFY_PRqIyHMk,893
90
90
  flwr/cli/supernode/create.py,sha256=MloB7Rr-CE_37jH5lFoM8OsaSPUzWt0TSSB_GMwND5E,1959
91
91
  flwr/cli/supernode/delete.py,sha256=_oCfBnoeUFPLEq_1EAqQp_CHEdzIEP2N_vHZsFTgWn0,1958
92
- flwr/cli/supernode/ls.py,sha256=j850KQEMpS0rdpL8m9icx7DlRMDRtns6I_iTwUudC_0,1801
92
+ flwr/cli/supernode/ls.py,sha256=KZZHQczmXi4vmgMJlBntD893fYQ9a7ZJum-8K_BPWAw,8550
93
93
  flwr/cli/utils.py,sha256=3bCu_MndtJD2S5MPG9VKi7SxG0No8S651pcfBVb29Y8,14397
94
94
  flwr/client/__init__.py,sha256=Q0MIF442vLIGSkcwHKq_sIfECQynLARJrumAscq2Q6E,1241
95
95
  flwr/client/client.py,sha256=3HAchxvknKG9jYbB7swNyDj-e5vUWDuMKoLvbT7jCVM,7895
@@ -223,8 +223,8 @@ flwr/proto/message_pb2.py,sha256=giymevXYEUdpIO-3A0XKsmRabXW1xSz0sIo5oOlbQ8Y,519
223
223
  flwr/proto/message_pb2.pyi,sha256=EzXZHy2mtabofrd_ZgKSI6M4QH-soIaRZIZBPwBGPv0,11260
224
224
  flwr/proto/message_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
225
225
  flwr/proto/message_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
226
- flwr/proto/node_pb2.py,sha256=gvFzmkEBB4Prp9y27RZKDoy1O5r56GqN-wpCdjoW5BA,1508
227
- flwr/proto/node_pb2.pyi,sha256=THEURTmlD-23XOULcu2AH6Mvy7pl2N9eWUSntCutO8o,2060
226
+ flwr/proto/node_pb2.py,sha256=_5UD-yH9Y-qtAP-gO3LZeDH_WoSFfjXmNvngsb9G-YM,1534
227
+ flwr/proto/node_pb2.pyi,sha256=kmLGmDIqudRtveUop_uVU6w696ahPB_b-O94k3HQIpU,2226
228
228
  flwr/proto/node_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
229
229
  flwr/proto/node_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
230
230
  flwr/proto/recorddict_pb2.py,sha256=eVkcnxMTFa3rvknRNiFuJ8z8xxPqgw7bV04aFiTe1j4,5290
@@ -413,7 +413,7 @@ flwr/superlink/servicer/control/control_account_auth_interceptor.py,sha256=Tbi4W
413
413
  flwr/superlink/servicer/control/control_event_log_interceptor.py,sha256=5uBl6VcJlUOgCF0d4kmsmJc1Rs1qxyouaZv0-uH2axs,5969
414
414
  flwr/superlink/servicer/control/control_grpc.py,sha256=xajQdX9WuKJmyXZJOaMZKeiSwgSCrile1NOErOiXR5w,4270
415
415
  flwr/superlink/servicer/control/control_license_interceptor.py,sha256=T3AzmRt-PPwyTq3hrdpmZHQd5_CpPOk7TtnFZrB-JRY,3349
416
- flwr/superlink/servicer/control/control_servicer.py,sha256=KUj45Fu5d6jugLWBkWzPre0l3TqD13NYQCWr-KeiSZ0,17134
416
+ flwr/superlink/servicer/control/control_servicer.py,sha256=DJaHBHNRS_9UVuqAk9ELu-1IfmFNVbt3SilYG1awaXQ,19092
417
417
  flwr/supernode/__init__.py,sha256=KgeCaVvXWrU3rptNR1y0oBp4YtXbAcrnCcJAiOoWkI4,707
418
418
  flwr/supernode/cli/__init__.py,sha256=JuEMr0-s9zv-PEWKuLB9tj1ocNfroSyNJ-oyv7ati9A,887
419
419
  flwr/supernode/cli/flower_supernode.py,sha256=7aBm0z03OU-npVd1onLCvUotyhSvlZLxAnFkGVMhZcw,8670
@@ -428,7 +428,7 @@ flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca
428
428
  flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
429
429
  flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=nIHRu38EWK-rpNOkcgBRAAKwYQQWFeCwu0lkO7OPZGQ,10239
430
430
  flwr/supernode/start_client_internal.py,sha256=Y9S1-QlO2WP6eo4JvWzIpfaCoh2aoE7bjEYyxNNnlyg,20777
431
- flwr_nightly-1.23.0.dev20251004.dist-info/METADATA,sha256=FPuw2y1PTpLtwP57ebIlqnjcBAPRKdklE0aTY43CYRE,14559
432
- flwr_nightly-1.23.0.dev20251004.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
433
- flwr_nightly-1.23.0.dev20251004.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
434
- flwr_nightly-1.23.0.dev20251004.dist-info/RECORD,,
431
+ flwr_nightly-1.23.0.dev20251006.dist-info/METADATA,sha256=BcU_7czuKJh5AnBvuRlID49gmN3-GSBQ01gQ2KtmLBQ,14559
432
+ flwr_nightly-1.23.0.dev20251006.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
433
+ flwr_nightly-1.23.0.dev20251006.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
434
+ flwr_nightly-1.23.0.dev20251006.dist-info/RECORD,,