flwr-nightly 1.17.0.dev20250311__py3-none-any.whl → 1.17.0.dev20250312__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.
@@ -35,7 +35,7 @@ warnings.filterwarnings("ignore", category=UserWarning)
35
35
  # pylint: disable=too-many-arguments
36
36
  # pylint: disable=too-many-instance-attributes
37
37
  class FlowerClient(NumPyClient):
38
- """Standard Flower client for CNN training."""
38
+ """Flower client for LLM fine-tuning."""
39
39
 
40
40
  def __init__(
41
41
  self,
@@ -0,0 +1,135 @@
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 Exec API event log interceptor."""
16
+
17
+
18
+ from collections.abc import Iterator
19
+ from typing import Any, Callable, Union, cast
20
+
21
+ import grpc
22
+ from google.protobuf.message import Message as GrpcMessage
23
+
24
+ from flwr.common.event_log_plugin.event_log_plugin import EventLogWriterPlugin
25
+ from flwr.common.typing import LogEntry
26
+
27
+ from .exec_user_auth_interceptor import shared_user_info
28
+
29
+
30
+ class ExecEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
31
+ """Exec API interceptor for logging events."""
32
+
33
+ def __init__(self, log_plugin: EventLogWriterPlugin) -> None:
34
+ self.log_plugin = log_plugin
35
+
36
+ def intercept_service(
37
+ self,
38
+ continuation: Callable[[Any], Any],
39
+ handler_call_details: grpc.HandlerCallDetails,
40
+ ) -> grpc.RpcMethodHandler:
41
+ """Flower server interceptor logging logic.
42
+
43
+ Intercept all unary-unary/unary-stream calls from users and log the event.
44
+ Continue RPC call if event logger is enabled on the SuperLink, else, terminate
45
+ RPC call by setting context to abort.
46
+ """
47
+ # One of the method handlers in
48
+ # `flwr.superexec.exec_servicer.ExecServicer`
49
+ method_handler: grpc.RpcMethodHandler = continuation(handler_call_details)
50
+ method_name: str = handler_call_details.method
51
+ return self._generic_event_log_unary_method_handler(method_handler, method_name)
52
+
53
+ def _generic_event_log_unary_method_handler(
54
+ self, method_handler: grpc.RpcMethodHandler, method_name: str
55
+ ) -> grpc.RpcMethodHandler:
56
+ def _generic_method_handler(
57
+ request: GrpcMessage,
58
+ context: grpc.ServicerContext,
59
+ ) -> Union[GrpcMessage, Iterator[GrpcMessage], Exception]:
60
+ log_entry: LogEntry
61
+ # Log before call
62
+ log_entry = self.log_plugin.compose_log_before_event(
63
+ request=request,
64
+ context=context,
65
+ user_info=shared_user_info.get(),
66
+ method_name=method_name,
67
+ )
68
+ self.log_plugin.write_log(log_entry)
69
+
70
+ # For unary-unary calls, log after the call immediately
71
+ if method_handler.unary_unary:
72
+ unary_response, error = None, None
73
+ try:
74
+ unary_response = cast(
75
+ GrpcMessage, method_handler.unary_unary(request, context)
76
+ )
77
+ except Exception as e: # pylint: disable=broad-except
78
+ error = e
79
+ raise
80
+ finally:
81
+ log_entry = self.log_plugin.compose_log_after_event(
82
+ request=request,
83
+ context=context,
84
+ user_info=shared_user_info.get(),
85
+ method_name=method_name,
86
+ response=unary_response or error,
87
+ )
88
+ self.log_plugin.write_log(log_entry)
89
+ return unary_response
90
+
91
+ # For unary-stream calls, wrap the response iterator and write the event log
92
+ # after iteration completes
93
+ if method_handler.unary_stream:
94
+ response_iterator = cast(
95
+ Iterator[GrpcMessage],
96
+ method_handler.unary_stream(request, context),
97
+ )
98
+
99
+ def response_wrapper() -> Iterator[GrpcMessage]:
100
+ stream_response, error = None, None
101
+ try:
102
+ # pylint: disable=use-yield-from
103
+ for stream_response in response_iterator:
104
+ yield stream_response
105
+ except Exception as e: # pylint: disable=broad-except
106
+ error = e
107
+ raise
108
+ finally:
109
+ # This block is executed after the client has consumed
110
+ # the entire stream, or if iteration is interrupted
111
+ log_entry = self.log_plugin.compose_log_after_event(
112
+ request=request,
113
+ context=context,
114
+ user_info=shared_user_info.get(),
115
+ method_name=method_name,
116
+ response=stream_response or error,
117
+ )
118
+ self.log_plugin.write_log(log_entry)
119
+
120
+ return response_wrapper()
121
+
122
+ raise RuntimeError() # This line is unreachable
123
+
124
+ if method_handler.unary_unary:
125
+ message_handler = grpc.unary_unary_rpc_method_handler
126
+ elif method_handler.unary_stream:
127
+ message_handler = grpc.unary_stream_rpc_method_handler
128
+ else:
129
+ # If the method type is not `unary_unary` or `unary_stream`, raise an error
130
+ raise NotImplementedError("This RPC method type is not supported.")
131
+ return message_handler(
132
+ _generic_method_handler,
133
+ request_deserializer=method_handler.request_deserializer,
134
+ response_serializer=method_handler.response_serializer,
135
+ )
@@ -15,11 +15,13 @@
15
15
  """Flower Exec API interceptor."""
16
16
 
17
17
 
18
- from typing import Any, Callable, Union
18
+ import contextvars
19
+ from typing import Any, Callable, Union, cast
19
20
 
20
21
  import grpc
21
22
 
22
23
  from flwr.common.auth_plugin import ExecAuthPlugin
24
+ from flwr.common.typing import UserInfo
23
25
  from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
24
26
  GetAuthTokensRequest,
25
27
  GetAuthTokensResponse,
@@ -43,6 +45,11 @@ Response = Union[
43
45
  ]
44
46
 
45
47
 
48
+ shared_user_info: contextvars.ContextVar[UserInfo] = contextvars.ContextVar(
49
+ "user_info", default=UserInfo(user_id=None, user_name=None)
50
+ )
51
+
52
+
46
53
  class ExecUserAuthInterceptor(grpc.ServerInterceptor): # type: ignore
47
54
  """Exec API interceptor for user authentication."""
48
55
 
@@ -77,13 +84,22 @@ class ExecUserAuthInterceptor(grpc.ServerInterceptor): # type: ignore
77
84
  ) -> Response:
78
85
  call = method_handler.unary_unary or method_handler.unary_stream
79
86
  metadata = context.invocation_metadata()
87
+
88
+ # Intercept GetLoginDetails and GetAuthTokens requests, and return
89
+ # the response without authentication
80
90
  if isinstance(request, (GetLoginDetailsRequest, GetAuthTokensRequest)):
81
91
  return call(request, context) # type: ignore
82
92
 
83
- valid_tokens, _ = self.auth_plugin.validate_tokens_in_metadata(metadata)
93
+ # For other requests, check if the user is authenticated
94
+ valid_tokens, user_info = self.auth_plugin.validate_tokens_in_metadata(
95
+ metadata
96
+ )
84
97
  if valid_tokens:
98
+ # Store user info in contextvars for authenticated users
99
+ shared_user_info.set(cast(UserInfo, user_info))
85
100
  return call(request, context) # type: ignore
86
101
 
102
+ # If the user is not authenticated, refresh tokens
87
103
  tokens = self.auth_plugin.refresh_tokens(context.invocation_metadata())
88
104
  if tokens is not None:
89
105
  context.send_initial_metadata(tokens)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.17.0.dev20250311
3
+ Version: 1.17.0.dev20250312
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -35,7 +35,7 @@ flwr/cli/new/templates/app/code/client.sklearn.py.tpl,sha256=MfhMN-hayGCc3cZ1XpN
35
35
  flwr/cli/new/templates/app/code/client.tensorflow.py.tpl,sha256=yBiiU7B9Kf70U52cPkNs_dUpYrrTwbUi2os-PAyheaM,1680
36
36
  flwr/cli/new/templates/app/code/dataset.baseline.py.tpl,sha256=jbd_exHAk2-Blu_kVutjPO6a_dkJQWb232zxSeXIZ1k,1453
37
37
  flwr/cli/new/templates/app/code/flwr_tune/__init__.py,sha256=JgNgBtKdm1jKM9625WxappCAVUGtYAmcjKSsXJ1u3ZQ,748
38
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl,sha256=Zgr_DwzP518E0lcGjs_togOeksdTVjwGU8sK6g8Wd0U,3765
38
+ flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl,sha256=tUyEAmkuUYop7teKNUO2NVbEB7-sXZBNLks8s6Wedws,3759
39
39
  flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl,sha256=1NA2Sf-EviNtOaYN4dnFk6v2tcZVsY3-eXY84wOXVng,3059
40
40
  flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl,sha256=ONJw_BgBWEofVNGRDu8KAIThb8saRQlUEK4uS2u_6To,2449
41
41
  flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl,sha256=xkmmBKr0oGmewP56SP3s_6FG6JOVlGlquhg3a9nYMis,3270
@@ -319,13 +319,14 @@ flwr/simulation/simulationio_connection.py,sha256=lcbEmdjb9RVEF2W5vSbf_J1zlTuv_Z
319
319
  flwr/superexec/__init__.py,sha256=fcj366jh4RFby_vDwLroU4kepzqbnJgseZD_jUr_Mko,715
320
320
  flwr/superexec/app.py,sha256=C0T2LMjuyF__I5V1FKfjtWtbsQPxK_EgL4vuhWIwG8s,1465
321
321
  flwr/superexec/deployment.py,sha256=wZ9G42gGS91knfplswh95MnQ83Fzu-rs6wcuNgDmmvY,6735
322
+ flwr/superexec/exec_event_log_interceptor.py,sha256=zFu1MQu3XkWa9PUTQgt5wb3BL_eR2dFS5M3BR5hbX34,5805
322
323
  flwr/superexec/exec_grpc.py,sha256=ttA9qoZzSLF0Mfk1L4hzOkMSNuj5rR58_kKBYwcyrAg,2864
323
324
  flwr/superexec/exec_servicer.py,sha256=4UpzJqPUHkBG2PZNe2lrX7XFVDOL6yw_HcoBHxuXE9A,8349
324
- flwr/superexec/exec_user_auth_interceptor.py,sha256=YtvcjrD2hMVmZ3y9wHuGI6FwmG_Y__q112t4fFnypy0,3793
325
+ flwr/superexec/exec_user_auth_interceptor.py,sha256=2kXjjJcrZyff893QTFLQD6zxC4pdVwtN4Rc66jHptfE,4440
325
326
  flwr/superexec/executor.py,sha256=_B55WW2TD1fBINpabSSDRenVHXYmvlfhv-k8hJKU4lQ,3115
326
327
  flwr/superexec/simulation.py,sha256=WQDon15oqpMopAZnwRZoTICYCfHqtkvFSqiTQ2hLD_g,4088
327
- flwr_nightly-1.17.0.dev20250311.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
328
- flwr_nightly-1.17.0.dev20250311.dist-info/METADATA,sha256=M4N9VKRlrUXFQAfvVh0EiUex2xAtt0reR7k8Um00mQA,15877
329
- flwr_nightly-1.17.0.dev20250311.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
330
- flwr_nightly-1.17.0.dev20250311.dist-info/entry_points.txt,sha256=2-1L-GNKhwGw2_7_RoH55vHw2SIHjdAQy3HAVAWl9PY,374
331
- flwr_nightly-1.17.0.dev20250311.dist-info/RECORD,,
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,,