flwr-nightly 1.13.0.dev20241029__py3-none-any.whl → 1.13.0.dev20241030__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/build.py CHANGED
@@ -81,8 +81,8 @@ def build(
81
81
  if not is_valid_project_name(app.name):
82
82
  typer.secho(
83
83
  f"❌ The project name {app.name} is invalid, "
84
- "a valid project name must start with a letter or an underscore, "
85
- "and can only contain letters, digits, and underscores.",
84
+ "a valid project name must start with a letter, "
85
+ "and can only contain letters, digits, and hyphens.",
86
86
  fg=typer.colors.RED,
87
87
  bold=True,
88
88
  )
flwr/cli/log.py CHANGED
@@ -18,29 +18,30 @@ import sys
18
18
  import time
19
19
  from logging import DEBUG, ERROR, INFO
20
20
  from pathlib import Path
21
- from typing import Annotated, Optional
21
+ from typing import Annotated, Optional, cast
22
22
 
23
23
  import grpc
24
24
  import typer
25
25
 
26
26
  from flwr.cli.config_utils import load_and_validate
27
+ from flwr.common.constant import CONN_RECONNECT_INTERVAL, CONN_REFRESH_PERIOD
27
28
  from flwr.common.grpc import GRPC_MAX_MESSAGE_LENGTH, create_channel
28
29
  from flwr.common.logger import log as logger
29
30
  from flwr.proto.exec_pb2 import StreamLogsRequest # pylint: disable=E0611
30
31
  from flwr.proto.exec_pb2_grpc import ExecStub
31
32
 
32
- CONN_REFRESH_PERIOD = 60 # Connection refresh period for log streaming (seconds)
33
-
34
33
 
35
34
  def start_stream(
36
35
  run_id: int, channel: grpc.Channel, refresh_period: int = CONN_REFRESH_PERIOD
37
36
  ) -> None:
38
37
  """Start log streaming for a given run ID."""
38
+ stub = ExecStub(channel)
39
+ after_timestamp = 0.0
39
40
  try:
41
+ logger(INFO, "Starting logstream for run_id `%s`", run_id)
40
42
  while True:
41
- logger(INFO, "Starting logstream for run_id `%s`", run_id)
42
- stream_logs(run_id, channel, refresh_period)
43
- time.sleep(2)
43
+ after_timestamp = stream_logs(run_id, stub, refresh_period, after_timestamp)
44
+ time.sleep(CONN_RECONNECT_INTERVAL)
44
45
  logger(DEBUG, "Reconnecting to logstream")
45
46
  except KeyboardInterrupt:
46
47
  logger(INFO, "Exiting logstream")
@@ -54,16 +55,44 @@ def start_stream(
54
55
  channel.close()
55
56
 
56
57
 
57
- def stream_logs(run_id: int, channel: grpc.Channel, duration: int) -> None:
58
- """Stream logs from the beginning of a run with connection refresh."""
59
- start_time = time.time()
60
- stub = ExecStub(channel)
61
- req = StreamLogsRequest(run_id=run_id)
58
+ def stream_logs(
59
+ run_id: int, stub: ExecStub, duration: int, after_timestamp: float
60
+ ) -> float:
61
+ """Stream logs from the beginning of a run with connection refresh.
62
+
63
+ Parameters
64
+ ----------
65
+ run_id : int
66
+ The identifier of the run.
67
+ stub : ExecStub
68
+ The gRPC stub to interact with the Exec service.
69
+ duration : int
70
+ The timeout duration for each stream connection in seconds.
71
+ after_timestamp : float
72
+ The timestamp to start streaming logs from.
73
+
74
+ Returns
75
+ -------
76
+ float
77
+ The latest timestamp from the streamed logs or the provided `after_timestamp`
78
+ if no logs are returned.
79
+ """
80
+ req = StreamLogsRequest(run_id=run_id, after_timestamp=after_timestamp)
81
+
82
+ latest_timestamp = 0.0
83
+ res = None
84
+ try:
85
+ for res in stub.StreamLogs(req, timeout=duration):
86
+ print(res.log_output, end="")
87
+ except grpc.RpcError as e:
88
+ # pylint: disable=E1101
89
+ if e.code() != grpc.StatusCode.DEADLINE_EXCEEDED:
90
+ raise e
91
+ finally:
92
+ if res is not None:
93
+ latest_timestamp = cast(float, res.latest_timestamp)
62
94
 
63
- for res in stub.StreamLogs(req):
64
- print(res.log_output)
65
- if time.time() - start_time > duration:
66
- break
95
+ return max(latest_timestamp, after_timestamp)
67
96
 
68
97
 
69
98
  def print_logs(run_id: int, channel: grpc.Channel, timeout: int) -> None:
@@ -181,11 +210,11 @@ def log(
181
210
  )
182
211
  raise typer.Exit(code=1)
183
212
 
184
- _log_with_superexec(federation_config, run_id, stream)
213
+ _log_with_exec_api(federation_config, run_id, stream)
185
214
 
186
215
 
187
216
  # pylint: disable-next=too-many-branches
188
- def _log_with_superexec(
217
+ def _log_with_exec_api(
189
218
  federation_config: dict[str, str],
190
219
  run_id: int,
191
220
  stream: bool,
flwr/common/constant.py CHANGED
@@ -87,6 +87,12 @@ MESSAGE_TTL_TOLERANCE = 1e-1
87
87
  ISOLATION_MODE_SUBPROCESS = "subprocess"
88
88
  ISOLATION_MODE_PROCESS = "process"
89
89
 
90
+ # Log streaming configurations
91
+ CONN_REFRESH_PERIOD = 60 # Stream connection refresh period
92
+ CONN_RECONNECT_INTERVAL = 0.5 # Reconnect interval between two stream connections
93
+ LOG_STREAM_INTERVAL = 0.5 # Log stream interval for `ExecServicer.StreamLogs`
94
+ LOG_UPLOAD_INTERVAL = 0.2 # Minimum interval between two log uploads
95
+
90
96
 
91
97
  class MessageType:
92
98
  """Message type."""
flwr/common/logger.py CHANGED
@@ -17,11 +17,21 @@
17
17
 
18
18
  import logging
19
19
  import sys
20
+ import threading
21
+ import time
20
22
  from logging import WARN, LogRecord
21
23
  from logging.handlers import HTTPHandler
22
- from queue import Queue
24
+ from queue import Empty, Queue
23
25
  from typing import TYPE_CHECKING, Any, Optional, TextIO
24
26
 
27
+ import grpc
28
+
29
+ from flwr.proto.driver_pb2_grpc import DriverStub # pylint: disable=E0611
30
+ from flwr.proto.log_pb2 import PushLogsRequest # pylint: disable=E0611
31
+ from flwr.proto.node_pb2 import Node # pylint: disable=E0611
32
+
33
+ from .constant import LOG_UPLOAD_INTERVAL
34
+
25
35
  # Create logger
26
36
  LOGGER_NAME = "flwr"
27
37
  FLOWER_LOGGER = logging.getLogger(LOGGER_NAME)
@@ -263,7 +273,7 @@ def set_logger_propagation(
263
273
  return child_logger
264
274
 
265
275
 
266
- def mirror_output_to_queue(log_queue: Queue[str]) -> None:
276
+ def mirror_output_to_queue(log_queue: Queue[Optional[str]]) -> None:
267
277
  """Mirror stdout and stderr output to the provided queue."""
268
278
 
269
279
  def get_write_fn(stream: TextIO) -> Any:
@@ -290,3 +300,65 @@ def restore_output() -> None:
290
300
  sys.stdout = sys.__stdout__
291
301
  sys.stderr = sys.__stderr__
292
302
  console_handler.stream = sys.stdout
303
+
304
+
305
+ def _log_uploader(
306
+ log_queue: Queue[Optional[str]], node_id: int, run_id: int, stub: DriverStub
307
+ ) -> None:
308
+ """Upload logs to the SuperLink."""
309
+ exit_flag = False
310
+ node = Node(node_id=node_id, anonymous=False)
311
+ msgs: list[str] = []
312
+ while True:
313
+ # Fetch all messages from the queue
314
+ try:
315
+ while True:
316
+ msg = log_queue.get_nowait()
317
+ # Quit the loops if the returned message is `None`
318
+ # This is a signal that the run has finished
319
+ if msg is None:
320
+ exit_flag = True
321
+ break
322
+ msgs.append(msg)
323
+ except Empty:
324
+ pass
325
+
326
+ # Upload if any logs
327
+ if msgs:
328
+ req = PushLogsRequest(
329
+ node=node,
330
+ run_id=run_id,
331
+ logs=msgs,
332
+ )
333
+ try:
334
+ stub.PushLogs(req)
335
+ msgs.clear()
336
+ except grpc.RpcError as e:
337
+ # Ignore minor network errors
338
+ # pylint: disable-next=no-member
339
+ if e.code() != grpc.StatusCode.UNAVAILABLE:
340
+ raise e
341
+
342
+ if exit_flag:
343
+ break
344
+
345
+ time.sleep(LOG_UPLOAD_INTERVAL)
346
+
347
+
348
+ def start_log_uploader(
349
+ log_queue: Queue[Optional[str]], node_id: int, run_id: int, stub: DriverStub
350
+ ) -> threading.Thread:
351
+ """Start the log uploader thread and return it."""
352
+ thread = threading.Thread(
353
+ target=_log_uploader, args=(log_queue, node_id, run_id, stub)
354
+ )
355
+ thread.start()
356
+ return thread
357
+
358
+
359
+ def stop_log_uploader(
360
+ log_queue: Queue[Optional[str]], log_uploader: threading.Thread
361
+ ) -> None:
362
+ """Stop the log uploader thread."""
363
+ log_queue.put(None)
364
+ log_uploader.join()
flwr/proto/driver_pb2.py CHANGED
@@ -20,7 +20,7 @@ from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
20
20
  from flwr.proto import fab_pb2 as flwr_dot_proto_dot_fab__pb2
21
21
 
22
22
 
23
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x66lwr/proto/driver.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/log.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x15\x66lwr/proto/task.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\"!\n\x0fGetNodesRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"3\n\x10GetNodesResponse\x12\x1f\n\x05nodes\x18\x01 \x03(\x0b\x32\x10.flwr.proto.Node\"@\n\x12PushTaskInsRequest\x12*\n\rtask_ins_list\x18\x01 \x03(\x0b\x32\x13.flwr.proto.TaskIns\"\'\n\x13PushTaskInsResponse\x12\x10\n\x08task_ids\x18\x02 \x03(\t\"F\n\x12PullTaskResRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x10\n\x08task_ids\x18\x02 \x03(\t\"A\n\x13PullTaskResResponse\x12*\n\rtask_res_list\x18\x01 \x03(\x0b\x32\x13.flwr.proto.TaskRes\",\n\x1aPullServerAppInputsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"\x7f\n\x1bPullServerAppInputsResponse\x12$\n\x07\x63ontext\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run\x12\x1c\n\x03\x66\x61\x62\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Fab\"S\n\x1bPushServerAppOutputsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12$\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Context\"\x1e\n\x1cPushServerAppOutputsResponse2\xc5\x06\n\x06\x44river\x12J\n\tCreateRun\x12\x1c.flwr.proto.CreateRunRequest\x1a\x1d.flwr.proto.CreateRunResponse\"\x00\x12G\n\x08GetNodes\x12\x1b.flwr.proto.GetNodesRequest\x1a\x1c.flwr.proto.GetNodesResponse\"\x00\x12P\n\x0bPushTaskIns\x12\x1e.flwr.proto.PushTaskInsRequest\x1a\x1f.flwr.proto.PushTaskInsResponse\"\x00\x12P\n\x0bPullTaskRes\x12\x1e.flwr.proto.PullTaskResRequest\x1a\x1f.flwr.proto.PullTaskResResponse\"\x00\x12\x41\n\x06GetRun\x12\x19.flwr.proto.GetRunRequest\x1a\x1a.flwr.proto.GetRunResponse\"\x00\x12\x41\n\x06GetFab\x12\x19.flwr.proto.GetFabRequest\x1a\x1a.flwr.proto.GetFabResponse\"\x00\x12h\n\x13PullServerAppInputs\x12&.flwr.proto.PullServerAppInputsRequest\x1a\'.flwr.proto.PullServerAppInputsResponse\"\x00\x12k\n\x14PushServerAppOutputs\x12\'.flwr.proto.PushServerAppOutputsRequest\x1a(.flwr.proto.PushServerAppOutputsResponse\"\x00\x12\\\n\x0fUpdateRunStatus\x12\".flwr.proto.UpdateRunStatusRequest\x1a#.flwr.proto.UpdateRunStatusResponse\"\x00\x12G\n\x08PushLogs\x12\x1b.flwr.proto.PushLogsRequest\x1a\x1c.flwr.proto.PushLogsResponse\"\x00\x62\x06proto3')
23
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x66lwr/proto/driver.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/log.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x15\x66lwr/proto/task.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\"!\n\x0fGetNodesRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"3\n\x10GetNodesResponse\x12\x1f\n\x05nodes\x18\x01 \x03(\x0b\x32\x10.flwr.proto.Node\"@\n\x12PushTaskInsRequest\x12*\n\rtask_ins_list\x18\x01 \x03(\x0b\x32\x13.flwr.proto.TaskIns\"\'\n\x13PushTaskInsResponse\x12\x10\n\x08task_ids\x18\x02 \x03(\t\"F\n\x12PullTaskResRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x10\n\x08task_ids\x18\x02 \x03(\t\"A\n\x13PullTaskResResponse\x12*\n\rtask_res_list\x18\x01 \x03(\x0b\x32\x13.flwr.proto.TaskRes\"\x1c\n\x1aPullServerAppInputsRequest\"\x7f\n\x1bPullServerAppInputsResponse\x12$\n\x07\x63ontext\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run\x12\x1c\n\x03\x66\x61\x62\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Fab\"S\n\x1bPushServerAppOutputsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12$\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Context\"\x1e\n\x1cPushServerAppOutputsResponse2\xc5\x06\n\x06\x44river\x12J\n\tCreateRun\x12\x1c.flwr.proto.CreateRunRequest\x1a\x1d.flwr.proto.CreateRunResponse\"\x00\x12G\n\x08GetNodes\x12\x1b.flwr.proto.GetNodesRequest\x1a\x1c.flwr.proto.GetNodesResponse\"\x00\x12P\n\x0bPushTaskIns\x12\x1e.flwr.proto.PushTaskInsRequest\x1a\x1f.flwr.proto.PushTaskInsResponse\"\x00\x12P\n\x0bPullTaskRes\x12\x1e.flwr.proto.PullTaskResRequest\x1a\x1f.flwr.proto.PullTaskResResponse\"\x00\x12\x41\n\x06GetRun\x12\x19.flwr.proto.GetRunRequest\x1a\x1a.flwr.proto.GetRunResponse\"\x00\x12\x41\n\x06GetFab\x12\x19.flwr.proto.GetFabRequest\x1a\x1a.flwr.proto.GetFabResponse\"\x00\x12h\n\x13PullServerAppInputs\x12&.flwr.proto.PullServerAppInputsRequest\x1a\'.flwr.proto.PullServerAppInputsResponse\"\x00\x12k\n\x14PushServerAppOutputs\x12\'.flwr.proto.PushServerAppOutputsRequest\x1a(.flwr.proto.PushServerAppOutputsResponse\"\x00\x12\\\n\x0fUpdateRunStatus\x12\".flwr.proto.UpdateRunStatusRequest\x1a#.flwr.proto.UpdateRunStatusResponse\"\x00\x12G\n\x08PushLogs\x12\x1b.flwr.proto.PushLogsRequest\x1a\x1c.flwr.proto.PushLogsResponse\"\x00\x62\x06proto3')
24
24
 
25
25
  _globals = globals()
26
26
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -40,13 +40,13 @@ if _descriptor._USE_C_DESCRIPTORS == False:
40
40
  _globals['_PULLTASKRESRESPONSE']._serialized_start=444
41
41
  _globals['_PULLTASKRESRESPONSE']._serialized_end=509
42
42
  _globals['_PULLSERVERAPPINPUTSREQUEST']._serialized_start=511
43
- _globals['_PULLSERVERAPPINPUTSREQUEST']._serialized_end=555
44
- _globals['_PULLSERVERAPPINPUTSRESPONSE']._serialized_start=557
45
- _globals['_PULLSERVERAPPINPUTSRESPONSE']._serialized_end=684
46
- _globals['_PUSHSERVERAPPOUTPUTSREQUEST']._serialized_start=686
47
- _globals['_PUSHSERVERAPPOUTPUTSREQUEST']._serialized_end=769
48
- _globals['_PUSHSERVERAPPOUTPUTSRESPONSE']._serialized_start=771
49
- _globals['_PUSHSERVERAPPOUTPUTSRESPONSE']._serialized_end=801
50
- _globals['_DRIVER']._serialized_start=804
51
- _globals['_DRIVER']._serialized_end=1641
43
+ _globals['_PULLSERVERAPPINPUTSREQUEST']._serialized_end=539
44
+ _globals['_PULLSERVERAPPINPUTSRESPONSE']._serialized_start=541
45
+ _globals['_PULLSERVERAPPINPUTSRESPONSE']._serialized_end=668
46
+ _globals['_PUSHSERVERAPPOUTPUTSREQUEST']._serialized_start=670
47
+ _globals['_PUSHSERVERAPPOUTPUTSREQUEST']._serialized_end=753
48
+ _globals['_PUSHSERVERAPPOUTPUTSRESPONSE']._serialized_start=755
49
+ _globals['_PUSHSERVERAPPOUTPUTSRESPONSE']._serialized_end=785
50
+ _globals['_DRIVER']._serialized_start=788
51
+ _globals['_DRIVER']._serialized_end=1625
52
52
  # @@protoc_insertion_point(module_scope)
flwr/proto/driver_pb2.pyi CHANGED
@@ -98,13 +98,8 @@ global___PullTaskResResponse = PullTaskResResponse
98
98
  class PullServerAppInputsRequest(google.protobuf.message.Message):
99
99
  """PullServerAppInputs messages"""
100
100
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
101
- RUN_ID_FIELD_NUMBER: builtins.int
102
- run_id: builtins.int
103
101
  def __init__(self,
104
- *,
105
- run_id: builtins.int = ...,
106
102
  ) -> None: ...
107
- def ClearField(self, field_name: typing_extensions.Literal["run_id",b"run_id"]) -> None: ...
108
103
  global___PullServerAppInputsRequest = PullServerAppInputsRequest
109
104
 
110
105
  class PullServerAppInputsResponse(google.protobuf.message.Message):
flwr/proto/exec_pb2.py CHANGED
@@ -16,7 +16,7 @@ from flwr.proto import fab_pb2 as flwr_dot_proto_dot_fab__pb2
16
16
  from flwr.proto import transport_pb2 as flwr_dot_proto_dot_transport__pb2
17
17
 
18
18
 
19
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x1a\x66lwr/proto/transport.proto\"\xdf\x02\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\x12L\n\x11\x66\x65\x64\x65ration_config\x18\x03 \x03(\x0b\x32\x31.flwr.proto.StartRunRequest.FederationConfigEntry\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\x1aK\n\x15\x46\x65\x64\x65rationConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"\"\n\x10StartRunResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"#\n\x11StreamLogsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"(\n\x12StreamLogsResponse\x12\x12\n\nlog_output\x18\x01 \x01(\t2\xa0\x01\n\x04\x45xec\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x12O\n\nStreamLogs\x12\x1d.flwr.proto.StreamLogsRequest\x1a\x1e.flwr.proto.StreamLogsResponse\"\x00\x30\x01\x62\x06proto3')
19
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x1a\x66lwr/proto/transport.proto\"\xdf\x02\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\x12L\n\x11\x66\x65\x64\x65ration_config\x18\x03 \x03(\x0b\x32\x31.flwr.proto.StartRunRequest.FederationConfigEntry\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\x1aK\n\x15\x46\x65\x64\x65rationConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"\"\n\x10StartRunResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"<\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\x32\xa0\x01\n\x04\x45xec\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x12O\n\nStreamLogs\x12\x1d.flwr.proto.StreamLogsRequest\x1a\x1e.flwr.proto.StreamLogsResponse\"\x00\x30\x01\x62\x06proto3')
20
20
 
21
21
  _globals = globals()
22
22
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -36,9 +36,9 @@ if _descriptor._USE_C_DESCRIPTORS == False:
36
36
  _globals['_STARTRUNRESPONSE']._serialized_start=441
37
37
  _globals['_STARTRUNRESPONSE']._serialized_end=475
38
38
  _globals['_STREAMLOGSREQUEST']._serialized_start=477
39
- _globals['_STREAMLOGSREQUEST']._serialized_end=512
40
- _globals['_STREAMLOGSRESPONSE']._serialized_start=514
41
- _globals['_STREAMLOGSRESPONSE']._serialized_end=554
42
- _globals['_EXEC']._serialized_start=557
43
- _globals['_EXEC']._serialized_end=717
39
+ _globals['_STREAMLOGSREQUEST']._serialized_end=537
40
+ _globals['_STREAMLOGSRESPONSE']._serialized_start=539
41
+ _globals['_STREAMLOGSRESPONSE']._serialized_end=605
42
+ _globals['_EXEC']._serialized_start=608
43
+ _globals['_EXEC']._serialized_end=768
44
44
  # @@protoc_insertion_point(module_scope)
flwr/proto/exec_pb2.pyi CHANGED
@@ -78,21 +78,27 @@ global___StartRunResponse = StartRunResponse
78
78
  class StreamLogsRequest(google.protobuf.message.Message):
79
79
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
80
80
  RUN_ID_FIELD_NUMBER: builtins.int
81
+ AFTER_TIMESTAMP_FIELD_NUMBER: builtins.int
81
82
  run_id: builtins.int
83
+ after_timestamp: builtins.float
82
84
  def __init__(self,
83
85
  *,
84
86
  run_id: builtins.int = ...,
87
+ after_timestamp: builtins.float = ...,
85
88
  ) -> None: ...
86
- def ClearField(self, field_name: typing_extensions.Literal["run_id",b"run_id"]) -> None: ...
89
+ def ClearField(self, field_name: typing_extensions.Literal["after_timestamp",b"after_timestamp","run_id",b"run_id"]) -> None: ...
87
90
  global___StreamLogsRequest = StreamLogsRequest
88
91
 
89
92
  class StreamLogsResponse(google.protobuf.message.Message):
90
93
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
91
94
  LOG_OUTPUT_FIELD_NUMBER: builtins.int
95
+ LATEST_TIMESTAMP_FIELD_NUMBER: builtins.int
92
96
  log_output: typing.Text
97
+ latest_timestamp: builtins.float
93
98
  def __init__(self,
94
99
  *,
95
100
  log_output: typing.Text = ...,
101
+ latest_timestamp: builtins.float = ...,
96
102
  ) -> None: ...
97
- def ClearField(self, field_name: typing_extensions.Literal["log_output",b"log_output"]) -> None: ...
103
+ def ClearField(self, field_name: typing_extensions.Literal["latest_timestamp",b"latest_timestamp","log_output",b"log_output"]) -> None: ...
98
104
  global___StreamLogsResponse = StreamLogsResponse
flwr/server/app.py CHANGED
@@ -382,9 +382,7 @@ def _flwr_serverapp_scheduler(
382
382
 
383
383
  log(
384
384
  INFO,
385
- "Launching `flwr-serverapp` subprocess with run-id %d. "
386
- "Connects to SuperLink on %s",
387
- pending_run_id,
385
+ "Launching `flwr-serverapp` subprocess. Connects to SuperLink on %s",
388
386
  driver_api_address,
389
387
  )
390
388
  # Start ServerApp subprocess
@@ -392,8 +390,6 @@ def _flwr_serverapp_scheduler(
392
390
  "flwr-serverapp",
393
391
  "--superlink",
394
392
  driver_api_address,
395
- "--run-id",
396
- str(pending_run_id),
397
393
  ]
398
394
  if ssl_ca_certfile:
399
395
  command.append("--root-certificates")
@@ -19,6 +19,7 @@ import sys
19
19
  from logging import DEBUG, ERROR, INFO, WARN
20
20
  from os.path import isfile
21
21
  from pathlib import Path
22
+ from queue import Queue
22
23
  from time import sleep
23
24
  from typing import Optional
24
25
 
@@ -31,7 +32,13 @@ from flwr.common.config import (
31
32
  get_project_dir,
32
33
  )
33
34
  from flwr.common.constant import Status, SubStatus
34
- from flwr.common.logger import log
35
+ from flwr.common.logger import (
36
+ log,
37
+ mirror_output_to_queue,
38
+ restore_output,
39
+ start_log_uploader,
40
+ stop_log_uploader,
41
+ )
35
42
  from flwr.common.serde import (
36
43
  context_from_proto,
37
44
  context_to_proto,
@@ -52,6 +59,10 @@ from flwr.server.run_serverapp import run as run_
52
59
 
53
60
  def flwr_serverapp() -> None:
54
61
  """Run process-isolated Flower ServerApp."""
62
+ # Capture stdout/stderr
63
+ log_queue: Queue[Optional[str]] = Queue()
64
+ mirror_output_to_queue(log_queue)
65
+
55
66
  log(INFO, "Starting Flower ServerApp")
56
67
 
57
68
  parser = argparse.ArgumentParser(
@@ -63,11 +74,10 @@ def flwr_serverapp() -> None:
63
74
  help="Address of SuperLink's DriverAPI",
64
75
  )
65
76
  parser.add_argument(
66
- "--run-id",
67
- type=int,
68
- required=False,
69
- help="Id of the Run this process should start. If not supplied, this "
70
- "function will request a pending run to the LinkState.",
77
+ "--run-once",
78
+ action="store_true",
79
+ help="When set, this process will start a single ServerApp "
80
+ "for a pending Run. If no pending run the process will exit. ",
71
81
  )
72
82
  parser.add_argument(
73
83
  "--flwr-dir",
@@ -100,18 +110,20 @@ def flwr_serverapp() -> None:
100
110
 
101
111
  log(
102
112
  DEBUG,
103
- "Staring isolated `ServerApp` connected to SuperLink DriverAPI at %s "
104
- "for run-id %s",
113
+ "Staring isolated `ServerApp` connected to SuperLink DriverAPI at %s",
105
114
  args.superlink,
106
- args.run_id,
107
115
  )
108
116
  run_serverapp(
109
117
  superlink=args.superlink,
110
- run_id=args.run_id,
118
+ log_queue=log_queue,
119
+ run_once=args.run_once,
111
120
  flwr_dir_=args.flwr_dir,
112
121
  certificates=certificates,
113
122
  )
114
123
 
124
+ # Restore stdout/stderr
125
+ restore_output()
126
+
115
127
 
116
128
  def _try_obtain_certificates(
117
129
  args: argparse.Namespace,
@@ -148,7 +160,8 @@ def _try_obtain_certificates(
148
160
 
149
161
  def run_serverapp( # pylint: disable=R0914, disable=W0212
150
162
  superlink: str,
151
- run_id: Optional[int] = None,
163
+ log_queue: Queue[Optional[str]],
164
+ run_once: bool,
152
165
  flwr_dir_: Optional[str] = None,
153
166
  certificates: Optional[bytes] = None,
154
167
  ) -> None:
@@ -160,18 +173,13 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
160
173
 
161
174
  # Resolve directory where FABs are installed
162
175
  flwr_dir = get_flwr_dir(flwr_dir_)
163
-
164
- only_once = run_id is not None
176
+ log_uploader = None
165
177
 
166
178
  while True:
167
179
 
168
180
  try:
169
181
  # Pull ServerAppInputs from LinkState
170
- req = (
171
- PullServerAppInputsRequest(run_id=run_id)
172
- if run_id
173
- else PullServerAppInputsRequest()
174
- )
182
+ req = PullServerAppInputsRequest()
175
183
  res: PullServerAppInputsResponse = driver._stub.PullServerAppInputs(req)
176
184
  if not res.HasField("run"):
177
185
  sleep(3)
@@ -184,6 +192,14 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
184
192
 
185
193
  driver.init_run(run.run_id)
186
194
 
195
+ # Start log uploader for this run
196
+ log_uploader = start_log_uploader(
197
+ log_queue=log_queue,
198
+ node_id=0,
199
+ run_id=run.run_id,
200
+ stub=driver._stub,
201
+ )
202
+
187
203
  log(DEBUG, "ServerApp process starts FAB installation.")
188
204
  install_from_fab(fab.content, flwr_dir=flwr_dir, skip_prompt=True)
189
205
 
@@ -245,9 +261,11 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
245
261
  )
246
262
  )
247
263
 
264
+ # Stop log uploader for this run
265
+ if log_uploader:
266
+ stop_log_uploader(log_queue, log_uploader)
267
+ log_uploader = None
268
+
248
269
  # Stop the loop if `flwr-serverapp` is expected to process a single run
249
- if only_once:
270
+ if run_once:
250
271
  break
251
-
252
- # Reset the run_id
253
- run_id = None
@@ -219,11 +219,8 @@ class DriverServicer(driver_pb2_grpc.DriverServicer):
219
219
 
220
220
  # Lock access to LinkState, preventing obtaining the same pending run_id
221
221
  with self.lock:
222
- # If run_id is provided, use it, otherwise use the pending run_id
223
- if _has_field(request, "run_id"):
224
- run_id: Optional[int] = request.run_id
225
- else:
226
- run_id = state.get_pending_run_id()
222
+ # Attempt getting the run_id of a pending run
223
+ run_id = state.get_pending_run_id()
227
224
  # If there's no pending run, return an empty response
228
225
  if run_id is None:
229
226
  return PullServerAppInputsResponse()
@@ -235,14 +232,12 @@ class DriverServicer(driver_pb2_grpc.DriverServicer):
235
232
  if run and run.fab_hash:
236
233
  if result := ffs.get(run.fab_hash):
237
234
  fab = Fab(run.fab_hash, result[0])
238
- if run and fab:
235
+ if run and fab and serverapp_ctxt:
239
236
  # Update run status to STARTING
240
237
  if state.update_run_status(run_id, RunStatus(Status.STARTING, "", "")):
241
238
  log(INFO, "Starting run %d", run_id)
242
239
  return PullServerAppInputsResponse(
243
- context=(
244
- context_to_proto(serverapp_ctxt) if serverapp_ctxt else None
245
- ),
240
+ context=context_to_proto(serverapp_ctxt),
246
241
  run=run_to_proto(run),
247
242
  fab=fab_to_proto(fab),
248
243
  )
@@ -278,7 +273,12 @@ class DriverServicer(driver_pb2_grpc.DriverServicer):
278
273
  ) -> PushLogsResponse:
279
274
  """Push logs."""
280
275
  log(DEBUG, "DriverServicer.PushLogs")
281
- raise NotImplementedError()
276
+ state = self.state_factory.state()
277
+
278
+ # Add logs to LinkState
279
+ merged_logs = "".join(request.logs)
280
+ state.add_serverapp_log(request.run_id, merged_logs)
281
+ return PushLogsResponse()
282
282
 
283
283
 
284
284
  def _raise_if(validation_error: bool, detail: str) -> None:
@@ -21,9 +21,10 @@ from typing import Optional
21
21
 
22
22
  from typing_extensions import override
23
23
 
24
- from flwr.common.constant import DRIVER_API_DEFAULT_ADDRESS
24
+ from flwr.common import Context, RecordSet
25
+ from flwr.common.constant import DRIVER_API_DEFAULT_ADDRESS, Status, SubStatus
25
26
  from flwr.common.logger import log
26
- from flwr.common.typing import Fab, UserConfig
27
+ from flwr.common.typing import Fab, RunStatus, UserConfig
27
28
  from flwr.server.superlink.ffs import Ffs
28
29
  from flwr.server.superlink.ffs.ffs_factory import FfsFactory
29
30
  from flwr.server.superlink.linkstate import LinkState, LinkStateFactory
@@ -135,6 +136,14 @@ class DeploymentEngine(Executor):
135
136
  run_id = self.linkstate.create_run(None, None, fab_hash, override_config)
136
137
  return run_id
137
138
 
139
+ def _create_context(self, run_id: int) -> None:
140
+ """Register a Context for a Run."""
141
+ # Create an empty context for the Run
142
+ context = Context(node_id=0, node_config={}, state=RecordSet(), run_config={})
143
+
144
+ # Register the context at the LinkState
145
+ self.linkstate.set_serverapp_context(run_id=run_id, context=context)
146
+
138
147
  @override
139
148
  def start_run(
140
149
  self,
@@ -143,18 +152,25 @@ class DeploymentEngine(Executor):
143
152
  federation_config: UserConfig,
144
153
  ) -> Optional[int]:
145
154
  """Start run using the Flower Deployment Engine."""
155
+ run_id = None
146
156
  try:
147
157
 
148
158
  # Call SuperLink to create run
149
- run_id: int = self._create_run(
159
+ run_id = self._create_run(
150
160
  Fab(hashlib.sha256(fab_file).hexdigest(), fab_file), override_config
151
161
  )
162
+
163
+ # Register context for the Run
164
+ self._create_context(run_id=run_id)
152
165
  log(INFO, "Created run %s", str(run_id))
153
166
 
154
167
  return run_id
155
168
  # pylint: disable-next=broad-except
156
169
  except Exception as e:
157
170
  log(ERROR, "Could not start run: %s", str(e))
171
+ if run_id:
172
+ run_status = RunStatus(Status.FINISHED, SubStatus.FAILED, str(e))
173
+ self.linkstate.update_run_status(run_id, new_status=run_status)
158
174
  return None
159
175
 
160
176
 
@@ -15,12 +15,14 @@
15
15
  """SuperExec API servicer."""
16
16
 
17
17
 
18
+ import time
18
19
  from collections.abc import Generator
19
20
  from logging import ERROR, INFO
20
21
  from typing import Any
21
22
 
22
23
  import grpc
23
24
 
25
+ from flwr.common.constant import LOG_STREAM_INTERVAL, Status
24
26
  from flwr.common.logger import log
25
27
  from flwr.common.serde import user_config_from_proto
26
28
  from flwr.proto import exec_pb2_grpc # pylint: disable=E0611
@@ -35,8 +37,6 @@ from flwr.server.superlink.linkstate import LinkStateFactory
35
37
 
36
38
  from .executor import Executor
37
39
 
38
- SELECT_TIMEOUT = 1 # Timeout for selecting ready-to-read file descriptors (in seconds)
39
-
40
40
 
41
41
  class ExecServicer(exec_pb2_grpc.ExecServicer):
42
42
  """SuperExec API servicer."""
@@ -75,5 +75,33 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
75
75
  ) -> Generator[StreamLogsResponse, Any, None]:
76
76
  """Get logs."""
77
77
  log(INFO, "ExecServicer.StreamLogs")
78
+ state = self.linkstate_factory.state()
79
+
80
+ # Retrieve run ID
81
+ run_id = request.run_id
82
+
83
+ # Exit if `run_id` not found
84
+ if not state.get_run(run_id):
85
+ context.abort(grpc.StatusCode.NOT_FOUND, "Run ID not found")
86
+
87
+ after_timestamp = request.after_timestamp + 1e-6
88
+ while context.is_active():
89
+ log_msg, latest_timestamp = state.get_serverapp_log(run_id, after_timestamp)
90
+ if log_msg:
91
+ yield StreamLogsResponse(
92
+ log_output=log_msg,
93
+ latest_timestamp=latest_timestamp,
94
+ )
95
+ # Add a small epsilon to the latest timestamp to avoid getting
96
+ # the same log
97
+ after_timestamp = max(latest_timestamp + 1e-6, after_timestamp)
98
+
99
+ # Wait for and continue to yield more log responses only if the
100
+ # run isn't completed yet. If the run is finished, the entire log
101
+ # is returned at this point and the server ends the stream.
102
+ run_status = state.get_run_status({run_id})[run_id]
103
+ if run_status.status == Status.FINISHED:
104
+ log(INFO, "All logs for run ID `%s` returned", request.run_id)
105
+ context.cancel()
78
106
 
79
- yield StreamLogsResponse()
107
+ time.sleep(LOG_STREAM_INTERVAL) # Sleep briefly to avoid busy waiting
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.13.0.dev20241029
3
+ Version: 1.13.0.dev20241030
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -1,11 +1,11 @@
1
1
  flwr/__init__.py,sha256=VmBWedrCxqmt4QvUHBLqyVEH6p7zaFMD_oCHerXHSVw,937
2
2
  flwr/cli/__init__.py,sha256=cZJVgozlkC6Ni2Hd_FAIrqefrkCGOV18fikToq-6iLw,720
3
3
  flwr/cli/app.py,sha256=_HDs7HS12Dp7NXIyVrkPs1SKJq3x-XvVZd6y1lvyud4,1255
4
- flwr/cli/build.py,sha256=WC7e6xPJJqRJvXmi8u0ECRvPThPYQcGOxLEgh9uG0gI,6365
4
+ flwr/cli/build.py,sha256=k2M0aIY2q5WB_yXQ22Woxt1zb6m-Z1wNwmhWMxEm5Dw,6344
5
5
  flwr/cli/config_utils.py,sha256=U0tYiC4uwT68LzXFpiiu6XzzplEo-43BR_ON9t3aHOw,7956
6
6
  flwr/cli/example.py,sha256=1bGDYll3BXQY2kRqSN-oICqS5n1b9m0g0RvXTopXHl4,2215
7
7
  flwr/cli/install.py,sha256=7Dx8zrn49mTktxGOToBhGx8hzsHOViDasMJ43ooKPXc,8646
8
- flwr/cli/log.py,sha256=uhtcLcFGkazirWnEmet3Wt3rt_q-a13kauQqPLaMaRY,8097
8
+ flwr/cli/log.py,sha256=SaHqtqI0xKRxt25s-ocQkKxhvXoLGkrsnXff3BBFPMI,9003
9
9
  flwr/cli/new/__init__.py,sha256=cQzK1WH4JP2awef1t2UQ2xjl1agVEz9rwutV18SWV1k,789
10
10
  flwr/cli/new/new.py,sha256=uSiG7aXQzPDnikv2YcjQ86OOLqint0hNWCI0fSQD0jI,9634
11
11
  flwr/cli/new/templates/__init__.py,sha256=4luU8RL-CK8JJCstQ_ON809W9bNTkY1l9zSaPKBkgwY,725
@@ -102,7 +102,7 @@ flwr/client/typing.py,sha256=dxoTBnTMfqXr5J7G3y-uNjqxYCddvxhu89spfj4Lm2U,1048
102
102
  flwr/common/__init__.py,sha256=TVaoFEJE158aui1TPZQiJCDZX4RNHRyI8I55VC80HhI,3901
103
103
  flwr/common/address.py,sha256=7kM2Rqjw86-c8aKwAvrXerWqznnVv4TFJ62aSAeTn10,3017
104
104
  flwr/common/config.py,sha256=nYA1vjiiqSWx5JjSdlQd1i_0N_Dh9kEGUse1Qze3JMs,7803
105
- flwr/common/constant.py,sha256=iv2O8vQdrIqsGy-RFluRDd0R0oaqWO046KPm14yPzw0,4376
105
+ flwr/common/constant.py,sha256=jWAHR2SRW_XJQ7ZMumFL54gSlo6Jp_sr1ndI5ebQNnI,4701
106
106
  flwr/common/context.py,sha256=5Bd9RCrhLkYZOVR7vr97OVhzVBHQkS1fUsYiIKTwpxU,2239
107
107
  flwr/common/date.py,sha256=uTvLmCkd3uVQuD4MviPHnIXMGyheL16mEI_UlOsv_R8,894
108
108
  flwr/common/differential_privacy.py,sha256=XwcJ3rWr8S8BZUocc76vLSJAXIf6OHnWkBV6-xlIRuw,6106
@@ -110,7 +110,7 @@ flwr/common/differential_privacy_constants.py,sha256=c7b7tqgvT7yMK0XN9ndiTBs4mQf
110
110
  flwr/common/dp.py,sha256=vddkvyjV2FhRoN4VuU2LeAM1UBn7dQB8_W-Qdiveal8,1978
111
111
  flwr/common/exit_handlers.py,sha256=MracJaBeoCOC7TaXK9zCJQxhrMSx9ZtczK237qvhBpU,2806
112
112
  flwr/common/grpc.py,sha256=6Yi28JjAll19nxYJlOT9B03RN8dvJZP9zUoR3RSmxoY,2487
113
- flwr/common/logger.py,sha256=0Whe3ce3anXNVQkJpRjM--nRnGum_UfYBsl4fACVuts,8959
113
+ flwr/common/logger.py,sha256=F5mfXVuxo0qfYIcWpxTkwkOeyhpUZ7oeDS9CUHxCibg,11045
114
114
  flwr/common/message.py,sha256=4O1m0OWXBAYZz05gKgEtnoJ94J1gjo7hCNHyUXThxRo,13831
115
115
  flwr/common/object_ref.py,sha256=5lgWqYaJR28UdFc-iirWw9YqFXMfgkOOAdfJc1AVibE,8711
116
116
  flwr/common/parameter.py,sha256=-bFAUayToYDF50FZGrBC1hQYJCQDtB2bbr3ZuVLMtdE,2095
@@ -149,16 +149,16 @@ flwr/proto/control_pb2.py,sha256=yaUkwY2J9uo-fdUIB5aHwVSDOuGunxaUr4ZlggifA_M,143
149
149
  flwr/proto/control_pb2.pyi,sha256=XbFvpZvvrS7QcH5AFXfpRGl4hQvhd3QdKO6x0oTlCCU,165
150
150
  flwr/proto/control_pb2_grpc.py,sha256=FFE21nZvEILWpe1WCR5vAwgYEtpzrdG78-_SsU0gZ7w,5783
151
151
  flwr/proto/control_pb2_grpc.pyi,sha256=9DU4sgkzJ497a4Nq6kitZWEG4g_5MO8MevichnO0oAg,1672
152
- flwr/proto/driver_pb2.py,sha256=B07TlCJaBFtfoekcyla2vpQ0b3JE0VoXgNSIkaRaBmw,4830
153
- flwr/proto/driver_pb2.pyi,sha256=Mq4xZ5Hn55CrUVLu4kvNZylKtF9AQ9NomOv1qJ3Py4Q,6636
152
+ flwr/proto/driver_pb2.py,sha256=2BEQT2YloRBX1-5Gp_rjKBiUD3SY7sr_ic6y0MGlsRk,4795
153
+ flwr/proto/driver_pb2.pyi,sha256=Ib9c32FCtjA9zZY54Ohi6B-DtLgSjokAqOExm_2uOvY,6429
154
154
  flwr/proto/driver_pb2_grpc.py,sha256=UgZ2PrrBmff-mwH1FG3Zf1YPYLoZVMldEBMUesjC-IQ,17154
155
155
  flwr/proto/driver_pb2_grpc.pyi,sha256=1yVGhlZFwR-o1hrGFJptMBrEpdjc_MYpD14oEWDqib8,4654
156
156
  flwr/proto/error_pb2.py,sha256=LarjKL90LbwkXKlhzNrDssgl4DXcvIPve8NVCXHpsKA,1084
157
157
  flwr/proto/error_pb2.pyi,sha256=ZNH4HhJTU_KfMXlyCeg8FwU-fcUYxTqEmoJPtWtHikc,734
158
158
  flwr/proto/error_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
159
159
  flwr/proto/error_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
160
- flwr/proto/exec_pb2.py,sha256=GH_VWC-BZwWmeWdmjP4IkXvwR8yY1uBZNNwKru-ZZL4,3231
161
- flwr/proto/exec_pb2.pyi,sha256=5y6L3xFkAuCfLTn2pVIHQAlXp17YcTTq8WVDS7_MPl4,4273
160
+ flwr/proto/exec_pb2.py,sha256=jrPfFdZrT7L6kI-MGu6Ej2rYpd8sgen27bm6njrTrrk,3335
161
+ flwr/proto/exec_pb2.pyi,sha256=rvf6cp7YXGQiltzjhd7ZeZmyAeHexDKTp_5kyavZYsA,4612
162
162
  flwr/proto/exec_pb2_grpc.py,sha256=faAN19XEMP8GTKrcIU6jvlWkN44n2KiUsZh_OG0sYcg,4072
163
163
  flwr/proto/exec_pb2_grpc.pyi,sha256=VrFhT1Um3Nb8UC2YqnR9GIiM-Yyx0FqaxVOWljh-G_w,1208
164
164
  flwr/proto/fab_pb2.py,sha256=3QSDq9pjbZoqVxsmCRDwHO5PrSjzn2vixjYxE-qPmb0,1589
@@ -203,7 +203,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
203
203
  flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
204
204
  flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
205
205
  flwr/server/__init__.py,sha256=cEg1oecBu4cKB69iJCqWEylC8b5XW47bl7rQiJsdTvM,1528
206
- flwr/server/app.py,sha256=7mV0p6Ej7GHyAnSyTOaaPJC2UuXpXfgNLuoDvLemsRM,28353
206
+ flwr/server/app.py,sha256=xt4o1Ls7BYEZ-R2Ro6_-zaBmv5yn2PtMH6p66mfZSmU,28222
207
207
  flwr/server/client_manager.py,sha256=7Ese0tgrH-i-ms363feYZJKwB8gWnXSmg_hYF2Bju4U,6227
208
208
  flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2399
209
209
  flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
@@ -222,7 +222,7 @@ flwr/server/server.py,sha256=1ZsFEptmAV-L2vP2etNC9Ed5CLSxpuKzUFkAPQ4l5Xc,17893
222
222
  flwr/server/server_app.py,sha256=RsgS6PRS5Z74cMUAHzsm8r3LWddwn00MjRs6rlacHt8,6297
223
223
  flwr/server/server_config.py,sha256=CZaHVAsMvGLjpWVcLPkiYxgJN4xfIyAiUrCI3fETKY4,1349
224
224
  flwr/server/serverapp/__init__.py,sha256=L0K-94UDdTyEZ8LDtYybGIIIv3HW6AhSVjXMUfYJQnQ,800
225
- flwr/server/serverapp/app.py,sha256=0cL0rL7AFr2SJwahKMs2xc8Eh6fa9IkrtJsJm4a6HN8,8464
225
+ flwr/server/serverapp/app.py,sha256=bCCfNmUYME1NQGcX8mNsTiI6WTFNHeXDJ7vHx4nmXtE,8979
226
226
  flwr/server/serverapp_components.py,sha256=-IV_CitOfrJclJj2jNdbN1Q65PyFmtKtrTIg1hc6WQw,2118
227
227
  flwr/server/strategy/__init__.py,sha256=tQer2SwjDnvgFFuJMZM-S01Z615N5XK6MaCvpm4BMU0,2836
228
228
  flwr/server/strategy/aggregate.py,sha256=iFZ8lp7PV_a2m9kywV-FK0iM33ofxavOs5TIaEQY8nU,13961
@@ -251,7 +251,7 @@ flwr/server/strategy/strategy.py,sha256=cXapkD5uDrt5C-RbmWDn9FLoap3Q41i7GKvbmfbC
251
251
  flwr/server/superlink/__init__.py,sha256=8tHYCfodUlRD8PCP9fHgvu8cz5N31A2QoRVL0jDJ15E,707
252
252
  flwr/server/superlink/driver/__init__.py,sha256=_JaRW-FdyikHc7souUrnk3mwTGViraEJCeUBY_M_ocs,712
253
253
  flwr/server/superlink/driver/driver_grpc.py,sha256=melAgaV37Y0B9bZe5bRWQOobItZZ9DIzlcbVE8B01wo,2060
254
- flwr/server/superlink/driver/driver_servicer.py,sha256=o-Ln3Ji2w7dXxP302PyZX-goEwiG71f1nJmHFuqjsqY,10563
254
+ flwr/server/superlink/driver/driver_servicer.py,sha256=n2De3kPaUwR9rFycWbpCp4mQILLshCNu9pUZW80_Ezc,10534
255
255
  flwr/server/superlink/ffs/__init__.py,sha256=FAY-zShcfPmOxosok2QyT6hTNMNctG8cH9s_nIl8jkI,840
256
256
  flwr/server/superlink/ffs/disk_ffs.py,sha256=yCN6CCzegnJIOaHr5nIu49wZQa4g5BByiSKshz50RKU,3296
257
257
  flwr/server/superlink/ffs/ffs.py,sha256=qLI1UfosJugu2BKOJWqHIhafTm-YiuKqGf3OGWPH0NM,2395
@@ -301,13 +301,13 @@ flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUq
301
301
  flwr/simulation/run_simulation.py,sha256=3n1nSik8tTC6LCYVZesNkHuXDQ1Ea4unTnNEOC5rdAc,23436
302
302
  flwr/superexec/__init__.py,sha256=fcj366jh4RFby_vDwLroU4kepzqbnJgseZD_jUr_Mko,715
303
303
  flwr/superexec/app.py,sha256=Tt3GonnTwHrMmicwx9XaP-crP78-bf4DUWl-N5cG6zY,1841
304
- flwr/superexec/deployment.py,sha256=fCAX2heyrraN-matKhHqFSaGKRVPEANAyTt43u8fddM,5678
304
+ flwr/superexec/deployment.py,sha256=jWEA8lnTYpOf8EHMzmoF9x6I_qh_gyJGQCVxk-DJshY,6401
305
305
  flwr/superexec/exec_grpc.py,sha256=OuhBAk7hiky9rjGceinLGIXqchtzGPQThZnwyYv6Ei0,2241
306
- flwr/superexec/exec_servicer.py,sha256=OcwB5XDMmjVHThY4Ucw1oKDaCw8t1dWlNlnUjkmdiWU,2633
306
+ flwr/superexec/exec_servicer.py,sha256=6dUCijBYhrntZeQj82q2kVOUNFu_tsFOwT5HkkLYn9Q,3927
307
307
  flwr/superexec/executor.py,sha256=QA2_hQJxmN3zc75oEkDs-zkWAHesz59jE0P5lem-5VU,3073
308
308
  flwr/superexec/simulation.py,sha256=Ny3MJnNlgzW4K3NbgsgDM0LKKcoCd_q3LqNqb0GhWLI,7640
309
- flwr_nightly-1.13.0.dev20241029.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
310
- flwr_nightly-1.13.0.dev20241029.dist-info/METADATA,sha256=Nq2hEJsE6LmIwcbUMIOJqRWWSbaDl66NmtBE9YxR12o,15618
311
- flwr_nightly-1.13.0.dev20241029.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
312
- flwr_nightly-1.13.0.dev20241029.dist-info/entry_points.txt,sha256=FxJQ96pmcNF2OvkTH6XF-Ip2PNrHvykjArkvkjQC7Mk,486
313
- flwr_nightly-1.13.0.dev20241029.dist-info/RECORD,,
309
+ flwr_nightly-1.13.0.dev20241030.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
310
+ flwr_nightly-1.13.0.dev20241030.dist-info/METADATA,sha256=Uz_4aLDl_yW5l5At_04N_oqhAid1FC9yx7sxME_0uLY,15618
311
+ flwr_nightly-1.13.0.dev20241030.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
312
+ flwr_nightly-1.13.0.dev20241030.dist-info/entry_points.txt,sha256=FxJQ96pmcNF2OvkTH6XF-Ip2PNrHvykjArkvkjQC7Mk,486
313
+ flwr_nightly-1.13.0.dev20241030.dist-info/RECORD,,