flwr-nightly 1.17.0.dev20250312__py3-none-any.whl → 1.17.0.dev20250313__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/common/constant.py CHANGED
@@ -217,14 +217,8 @@ class AuthType:
217
217
  class EventLogWriterType:
218
218
  """Event log writer types."""
219
219
 
220
- FALSE = "false"
221
220
  STDOUT = "stdout"
222
221
 
223
222
  def __new__(cls) -> EventLogWriterType:
224
223
  """Prevent instantiation."""
225
224
  raise TypeError(f"{cls.__name__} cannot be instantiated.")
226
-
227
- @classmethod
228
- def choices(cls) -> list[str]:
229
- """Return a list of available log writer choices."""
230
- return [cls.FALSE, cls.STDOUT]
@@ -36,7 +36,7 @@ class EventLogWriterPlugin(ABC):
36
36
  self,
37
37
  request: GrpcMessage,
38
38
  context: grpc.ServicerContext,
39
- user_info: UserInfo,
39
+ user_info: Optional[UserInfo],
40
40
  method_name: str,
41
41
  ) -> LogEntry:
42
42
  """Compose pre-event log entry from the provided request and context."""
@@ -46,7 +46,7 @@ class EventLogWriterPlugin(ABC):
46
46
  self,
47
47
  request: GrpcMessage,
48
48
  context: grpc.ServicerContext,
49
- user_info: UserInfo,
49
+ user_info: Optional[UserInfo],
50
50
  method_name: str,
51
51
  response: Optional[Union[GrpcMessage, Exception]],
52
52
  ) -> LogEntry:
flwr/server/app.py CHANGED
@@ -54,7 +54,9 @@ from flwr.common.constant import (
54
54
  TRANSPORT_TYPE_GRPC_ADAPTER,
55
55
  TRANSPORT_TYPE_GRPC_RERE,
56
56
  TRANSPORT_TYPE_REST,
57
+ EventLogWriterType,
57
58
  )
59
+ from flwr.common.event_log_plugin import EventLogWriterPlugin
58
60
  from flwr.common.exit import ExitCode, flwr_exit
59
61
  from flwr.common.exit_handlers import register_exit_handlers
60
62
  from flwr.common.grpc import generic_create_grpc_server
@@ -66,6 +68,7 @@ from flwr.proto.fleet_pb2_grpc import ( # pylint: disable=E0611
66
68
  add_FleetServicer_to_server,
67
69
  )
68
70
  from flwr.proto.grpcadapter_pb2_grpc import add_GrpcAdapterServicer_to_server
71
+ from flwr.server.fleet_event_log_interceptor import FleetEventLogInterceptor
69
72
  from flwr.server.serverapp.app import flwr_serverapp
70
73
  from flwr.simulation.app import flwr_simulation
71
74
  from flwr.superexec.app import load_executor
@@ -94,6 +97,8 @@ try:
94
97
  add_ee_args_superlink,
95
98
  get_dashboard_server,
96
99
  get_exec_auth_plugins,
100
+ get_exec_event_log_writer_plugins,
101
+ get_fleet_event_log_writer_plugins,
97
102
  )
98
103
  except ImportError:
99
104
 
@@ -105,6 +110,18 @@ except ImportError:
105
110
  """Return all Exec API authentication plugins."""
106
111
  raise NotImplementedError("No authentication plugins are currently supported.")
107
112
 
113
+ def get_exec_event_log_writer_plugins() -> dict[str, type[EventLogWriterPlugin]]:
114
+ """Return all Exec API event log writer plugins."""
115
+ raise NotImplementedError(
116
+ "No event log writer plugins are currently supported."
117
+ )
118
+
119
+ def get_fleet_event_log_writer_plugins() -> dict[str, type[EventLogWriterPlugin]]:
120
+ """Return all Fleet API event log writer plugins."""
121
+ raise NotImplementedError(
122
+ "No event log writer plugins are currently supported."
123
+ )
124
+
108
125
 
109
126
  def start_server( # pylint: disable=too-many-arguments,too-many-locals
110
127
  *,
@@ -276,9 +293,13 @@ def run_superlink() -> None:
276
293
  verify_tls_cert = not getattr(args, "disable_oidc_tls_cert_verification", None)
277
294
 
278
295
  auth_plugin: Optional[ExecAuthPlugin] = None
296
+ event_log_plugin: Optional[EventLogWriterPlugin] = None
279
297
  # Load the auth plugin if the args.user_auth_config is provided
280
298
  if cfg_path := getattr(args, "user_auth_config", None):
281
299
  auth_plugin = _try_obtain_exec_auth_plugin(Path(cfg_path), verify_tls_cert)
300
+ # Enable event logging if the args.enable_event_log is True
301
+ if args.enable_event_log:
302
+ event_log_plugin = _try_obtain_exec_event_log_writer_plugin()
282
303
 
283
304
  # Initialize StateFactory
284
305
  state_factory = LinkStateFactory(args.database)
@@ -298,6 +319,7 @@ def run_superlink() -> None:
298
319
  [args.executor_config] if args.executor_config else args.executor_config
299
320
  ),
300
321
  auth_plugin=auth_plugin,
322
+ event_log_plugin=event_log_plugin,
301
323
  )
302
324
  grpc_servers = [exec_server]
303
325
 
@@ -389,6 +411,11 @@ def run_superlink() -> None:
389
411
  log(DEBUG, "Automatic node authentication enabled")
390
412
 
391
413
  interceptors = [AuthenticateServerInterceptor(state_factory, auto_auth)]
414
+ if getattr(args, "enable_event_log", None):
415
+ fleet_log_plugin = _try_obtain_fleet_event_log_writer_plugin()
416
+ if fleet_log_plugin is not None:
417
+ interceptors.append(FleetEventLogInterceptor(fleet_log_plugin))
418
+ log(INFO, "Flower Fleet event logging enabled")
392
419
 
393
420
  fleet_server = _run_fleet_api_grpc_rere(
394
421
  address=fleet_address,
@@ -613,6 +640,34 @@ def _try_obtain_exec_auth_plugin(
613
640
  sys.exit("No authentication plugins are currently supported.")
614
641
 
615
642
 
643
+ def _try_obtain_exec_event_log_writer_plugin() -> Optional[EventLogWriterPlugin]:
644
+ """Return an instance of the event log writer plugin."""
645
+ try:
646
+ all_plugins: dict[str, type[EventLogWriterPlugin]] = (
647
+ get_exec_event_log_writer_plugins()
648
+ )
649
+ plugin_class = all_plugins[EventLogWriterType.STDOUT]
650
+ return plugin_class()
651
+ except KeyError:
652
+ sys.exit("No event log writer plugin is provided.")
653
+ except NotImplementedError:
654
+ sys.exit("No event log writer plugins are currently supported.")
655
+
656
+
657
+ def _try_obtain_fleet_event_log_writer_plugin() -> Optional[EventLogWriterPlugin]:
658
+ """Return an instance of the Fleet Servicer event log writer plugin."""
659
+ try:
660
+ all_plugins: dict[str, type[EventLogWriterPlugin]] = (
661
+ get_fleet_event_log_writer_plugins()
662
+ )
663
+ plugin_class = all_plugins[EventLogWriterType.STDOUT]
664
+ return plugin_class()
665
+ except KeyError:
666
+ sys.exit("No Fleet API event log writer plugin is provided.")
667
+ except NotImplementedError:
668
+ sys.exit("No Fleet API event log writer plugins are currently supported.")
669
+
670
+
616
671
  def _run_fleet_api_grpc_rere(
617
672
  address: str,
618
673
  state_factory: LinkStateFactory,
@@ -0,0 +1,94 @@
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
+ """Flower Fleet API event log interceptor."""
16
+
17
+
18
+ from typing import Any, Callable, cast
19
+
20
+ import grpc
21
+ from google.protobuf.message import Message as GrpcMessage
22
+
23
+ from flwr.common.event_log_plugin.event_log_plugin import EventLogWriterPlugin
24
+ from flwr.common.typing import LogEntry
25
+
26
+
27
+ class FleetEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
28
+ """Fleet API interceptor for logging events."""
29
+
30
+ def __init__(self, log_plugin: EventLogWriterPlugin) -> None:
31
+ self.log_plugin = log_plugin
32
+
33
+ def intercept_service(
34
+ self,
35
+ continuation: Callable[[Any], Any],
36
+ handler_call_details: grpc.HandlerCallDetails,
37
+ ) -> grpc.RpcMethodHandler:
38
+ """Flower Fleet API server interceptor logging logic.
39
+
40
+ Intercept all unary-unary calls from users and log the event. Continue RPC call
41
+ if event logger is enabled on the SuperLink, else, terminate RPC call by setting
42
+ context to abort.
43
+ """
44
+ # One of the method handlers in
45
+ # `flwr.server.superlink.fleet.grpc_rere.fleet_servicer.FleetServicer`
46
+ method_handler: grpc.RpcMethodHandler = continuation(handler_call_details)
47
+ method_name: str = handler_call_details.method
48
+ return self._generic_event_log_unary_method_handler(method_handler, method_name)
49
+
50
+ def _generic_event_log_unary_method_handler(
51
+ self, method_handler: grpc.RpcMethodHandler, method_name: str
52
+ ) -> grpc.RpcMethodHandler:
53
+ def _generic_method_handler(
54
+ request: GrpcMessage,
55
+ context: grpc.ServicerContext,
56
+ ) -> GrpcMessage:
57
+ log_entry: LogEntry
58
+ # Log before call
59
+ log_entry = self.log_plugin.compose_log_before_event(
60
+ request=request,
61
+ context=context,
62
+ user_info=None,
63
+ method_name=method_name,
64
+ )
65
+ self.log_plugin.write_log(log_entry)
66
+
67
+ call = method_handler.unary_unary
68
+ unary_response, error = None, None
69
+ try:
70
+ unary_response = cast(GrpcMessage, call(request, context))
71
+ except Exception as e: # pylint: disable=broad-except
72
+ error = e
73
+ raise
74
+ finally:
75
+ log_entry = self.log_plugin.compose_log_after_event(
76
+ request=request,
77
+ context=context,
78
+ user_info=None,
79
+ method_name=method_name,
80
+ response=unary_response or error,
81
+ )
82
+ self.log_plugin.write_log(log_entry)
83
+ return unary_response
84
+
85
+ if method_handler.unary_unary:
86
+ message_handler = grpc.unary_unary_rpc_method_handler
87
+ else:
88
+ # If the method type is not `unary_unary` raise an error
89
+ raise NotImplementedError("This RPC method type is not supported.")
90
+ return message_handler(
91
+ _generic_method_handler,
92
+ request_deserializer=method_handler.request_deserializer,
93
+ response_serializer=method_handler.response_serializer,
94
+ )
@@ -15,7 +15,6 @@
15
15
  """SuperExec gRPC API."""
16
16
 
17
17
 
18
- from collections.abc import Sequence
19
18
  from logging import INFO
20
19
  from typing import Optional
21
20
 
@@ -23,12 +22,14 @@ import grpc
23
22
 
24
23
  from flwr.common import GRPC_MAX_MESSAGE_LENGTH
25
24
  from flwr.common.auth_plugin import ExecAuthPlugin
25
+ from flwr.common.event_log_plugin import EventLogWriterPlugin
26
26
  from flwr.common.grpc import generic_create_grpc_server
27
27
  from flwr.common.logger import log
28
28
  from flwr.common.typing import UserConfig
29
29
  from flwr.proto.exec_pb2_grpc import add_ExecServicer_to_server
30
30
  from flwr.server.superlink.ffs.ffs_factory import FfsFactory
31
31
  from flwr.server.superlink.linkstate import LinkStateFactory
32
+ from flwr.superexec.exec_event_log_interceptor import ExecEventLogInterceptor
32
33
  from flwr.superexec.exec_user_auth_interceptor import ExecUserAuthInterceptor
33
34
 
34
35
  from .exec_servicer import ExecServicer
@@ -44,6 +45,7 @@ def run_exec_api_grpc(
44
45
  certificates: Optional[tuple[bytes, bytes, bytes]],
45
46
  config: UserConfig,
46
47
  auth_plugin: Optional[ExecAuthPlugin] = None,
48
+ event_log_plugin: Optional[EventLogWriterPlugin] = None,
47
49
  ) -> grpc.Server:
48
50
  """Run Exec API (gRPC, request-response)."""
49
51
  executor.set_config(config)
@@ -54,16 +56,20 @@ def run_exec_api_grpc(
54
56
  executor=executor,
55
57
  auth_plugin=auth_plugin,
56
58
  )
57
- interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None
59
+ interceptors: list[grpc.ServerInterceptor] = []
58
60
  if auth_plugin is not None:
59
- interceptors = [ExecUserAuthInterceptor(auth_plugin)]
61
+ interceptors.append(ExecUserAuthInterceptor(auth_plugin))
62
+ # Event log interceptor must be added after user auth interceptor
63
+ if event_log_plugin is not None:
64
+ interceptors.append(ExecEventLogInterceptor(event_log_plugin))
65
+ log(INFO, "Flower event logging enabled")
60
66
  exec_add_servicer_to_server_fn = add_ExecServicer_to_server
61
67
  exec_grpc_server = generic_create_grpc_server(
62
68
  servicer_and_add_fn=(exec_servicer, exec_add_servicer_to_server_fn),
63
69
  server_address=address,
64
70
  max_message_length=GRPC_MAX_MESSAGE_LENGTH,
65
71
  certificates=certificates,
66
- interceptors=interceptors,
72
+ interceptors=interceptors or None,
67
73
  )
68
74
 
69
75
  if auth_plugin is None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.17.0.dev20250312
3
+ Version: 1.17.0.dev20250313
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -116,14 +116,14 @@ flwr/common/args.py,sha256=2gGT2a3SPJ0-LTNKnhBsZ-ESIoW9FGpw-9xkUSs8qwk,5417
116
116
  flwr/common/auth_plugin/__init__.py,sha256=1Y8Oj3iB49IHDu9tvDih1J74Ygu7k85V9s2A4WORPyA,887
117
117
  flwr/common/auth_plugin/auth_plugin.py,sha256=dQU5U4uJIA5XqgOJ3PankHWq-uXCaMvO74khaMPGdiU,3938
118
118
  flwr/common/config.py,sha256=SAkG3BztnA6iupXxF3GAIpGmWVVCH0ptyMpC9yjr_14,13965
119
- flwr/common/constant.py,sha256=6NtDbh_RgQebtPfn01a8yN_7poMOuwy6jbtVLQdBbQc,7026
119
+ flwr/common/constant.py,sha256=lAnqe63ZzW0NcS306rnEVICpJSXCFNoPj4L1X-p0FCA,6853
120
120
  flwr/common/context.py,sha256=uJ-mnoC_8y_udEb3kAX-r8CPphNTWM72z1AlsvQEu54,2403
121
121
  flwr/common/date.py,sha256=NHHpESce5wYqEwoDXf09gp9U9l_5Bmlh2BsOcwS-kDM,1554
122
122
  flwr/common/differential_privacy.py,sha256=YA01NqjddKNAEVmf7hXmOVxOjhekgzvJudk3mBGq-2k,6148
123
123
  flwr/common/differential_privacy_constants.py,sha256=c7b7tqgvT7yMK0XN9ndiTBs4mQf6d3qk6K7KBZGlV4Q,1074
124
124
  flwr/common/dp.py,sha256=vddkvyjV2FhRoN4VuU2LeAM1UBn7dQB8_W-Qdiveal8,1978
125
125
  flwr/common/event_log_plugin/__init__.py,sha256=ts3VAL3Fk6Grp1EK_1Qg_V-BfOof9F86iBx4rbrEkyo,838
126
- flwr/common/event_log_plugin/event_log_plugin.py,sha256=PyNdJglG-A2VAH0gTXQO7TMImr24uAHMzNBXwnFmnmE,2022
126
+ flwr/common/event_log_plugin/event_log_plugin.py,sha256=aGRc_MKHIjxkAP5nYf9wXWpcU-DNhwUH-qzhZG8MRIQ,2042
127
127
  flwr/common/exit/__init__.py,sha256=-ZOJYLaNnR729a7VzZiFsLiqngzKQh3xc27svYStZ_Q,826
128
128
  flwr/common/exit/exit.py,sha256=DmZFyksp-w1sFDQekq5Z-qfnr-ivCAv78aQkqj-TDps,3458
129
129
  flwr/common/exit/exit_code.py,sha256=PNEnCrZfOILjfDAFu5m-2YWEJBrk97xglq4zCUlqV7E,3470
@@ -214,7 +214,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
214
214
  flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
215
215
  flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
216
216
  flwr/server/__init__.py,sha256=cEg1oecBu4cKB69iJCqWEylC8b5XW47bl7rQiJsdTvM,1528
217
- flwr/server/app.py,sha256=5GIzaP5gyQLy6Qi8_ePLaZmyhfa3ukSylWUZ5poMtNc,31086
217
+ flwr/server/app.py,sha256=yCfZSKCSu2w7jJYIj-dpF0inyPpsY-KkjgVcmMVcZiY,33614
218
218
  flwr/server/client_manager.py,sha256=7Ese0tgrH-i-ms363feYZJKwB8gWnXSmg_hYF2Bju4U,6227
219
219
  flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2399
220
220
  flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
@@ -227,6 +227,7 @@ flwr/server/driver/__init__.py,sha256=bikRv6CjTwSvYh7tf10gziU5o2YotOWhhftz2tr3KD
227
227
  flwr/server/driver/driver.py,sha256=X072eFWl8Kx-aZbahTkpAc1wwoojr8A4uO2yozwwSbE,5705
228
228
  flwr/server/driver/grpc_driver.py,sha256=u3kj9Ej5vErlRcdeF_8giqVXWLP0obT405Kjv6vC-Vw,10298
229
229
  flwr/server/driver/inmemory_driver.py,sha256=p6p9RykDfoty94izzD4i11Xp7A8t1KUaHpbKbbVZAdU,6407
230
+ flwr/server/fleet_event_log_interceptor.py,sha256=IIZFv4tAudDpLsRTkU5DWVEQVUZupKRBZl-ktK0b6UU,3802
230
231
  flwr/server/history.py,sha256=qSb5_pPTrwofpSYGsZWzMPkl_4uJ4mJFWesxXDrEvDU,5026
231
232
  flwr/server/run_serverapp.py,sha256=tyAYB5UEiUd63VG4XiYBUUiivh77SpIol1cGS4xtYdk,2077
232
233
  flwr/server/server.py,sha256=1ZsFEptmAV-L2vP2etNC9Ed5CLSxpuKzUFkAPQ4l5Xc,17893
@@ -320,13 +321,13 @@ flwr/superexec/__init__.py,sha256=fcj366jh4RFby_vDwLroU4kepzqbnJgseZD_jUr_Mko,71
320
321
  flwr/superexec/app.py,sha256=C0T2LMjuyF__I5V1FKfjtWtbsQPxK_EgL4vuhWIwG8s,1465
321
322
  flwr/superexec/deployment.py,sha256=wZ9G42gGS91knfplswh95MnQ83Fzu-rs6wcuNgDmmvY,6735
322
323
  flwr/superexec/exec_event_log_interceptor.py,sha256=zFu1MQu3XkWa9PUTQgt5wb3BL_eR2dFS5M3BR5hbX34,5805
323
- flwr/superexec/exec_grpc.py,sha256=ttA9qoZzSLF0Mfk1L4hzOkMSNuj5rR58_kKBYwcyrAg,2864
324
+ flwr/superexec/exec_grpc.py,sha256=9-WF6qPuhiYohMe3SIZ5Sapp_dLjICvm5QfdTNizsJg,3252
324
325
  flwr/superexec/exec_servicer.py,sha256=4UpzJqPUHkBG2PZNe2lrX7XFVDOL6yw_HcoBHxuXE9A,8349
325
326
  flwr/superexec/exec_user_auth_interceptor.py,sha256=2kXjjJcrZyff893QTFLQD6zxC4pdVwtN4Rc66jHptfE,4440
326
327
  flwr/superexec/executor.py,sha256=_B55WW2TD1fBINpabSSDRenVHXYmvlfhv-k8hJKU4lQ,3115
327
328
  flwr/superexec/simulation.py,sha256=WQDon15oqpMopAZnwRZoTICYCfHqtkvFSqiTQ2hLD_g,4088
328
- flwr_nightly-1.17.0.dev20250312.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
329
- flwr_nightly-1.17.0.dev20250312.dist-info/METADATA,sha256=uOd2BEE053PBIRj-JOYG48jU4qASw1D_XJLThzPbnW4,15877
330
- flwr_nightly-1.17.0.dev20250312.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
331
- flwr_nightly-1.17.0.dev20250312.dist-info/entry_points.txt,sha256=2-1L-GNKhwGw2_7_RoH55vHw2SIHjdAQy3HAVAWl9PY,374
332
- flwr_nightly-1.17.0.dev20250312.dist-info/RECORD,,
329
+ flwr_nightly-1.17.0.dev20250313.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
330
+ flwr_nightly-1.17.0.dev20250313.dist-info/METADATA,sha256=iAYJEWp0Rxernl7OApLQ2wsxqmysOtJ0UJ7qpO3wv9Y,15877
331
+ flwr_nightly-1.17.0.dev20250313.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
332
+ flwr_nightly-1.17.0.dev20250313.dist-info/entry_points.txt,sha256=2-1L-GNKhwGw2_7_RoH55vHw2SIHjdAQy3HAVAWl9PY,374
333
+ flwr_nightly-1.17.0.dev20250313.dist-info/RECORD,,