flwr 1.15.2__py3-none-any.whl → 1.17.0__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/build.py +2 -0
- flwr/cli/log.py +20 -21
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/run/run.py +5 -9
- flwr/client/app.py +6 -4
- flwr/client/client_app.py +260 -86
- flwr/client/clientapp/app.py +6 -2
- flwr/client/grpc_client/connection.py +24 -21
- flwr/client/message_handler/message_handler.py +28 -28
- flwr/client/mod/__init__.py +2 -2
- flwr/client/mod/centraldp_mods.py +7 -7
- flwr/client/mod/comms_mods.py +16 -22
- flwr/client/mod/localdp_mod.py +4 -4
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +31 -31
- flwr/client/rest_client/connection.py +4 -6
- flwr/client/run_info_store.py +2 -2
- flwr/client/supernode/__init__.py +0 -2
- flwr/client/supernode/app.py +1 -11
- flwr/common/__init__.py +12 -4
- flwr/common/address.py +35 -0
- flwr/common/args.py +8 -2
- flwr/common/auth_plugin/auth_plugin.py +2 -1
- flwr/common/config.py +4 -4
- flwr/common/constant.py +16 -0
- flwr/common/context.py +4 -4
- flwr/common/event_log_plugin/__init__.py +22 -0
- flwr/common/event_log_plugin/event_log_plugin.py +60 -0
- flwr/common/grpc.py +1 -1
- flwr/common/logger.py +2 -2
- flwr/common/message.py +338 -102
- flwr/common/object_ref.py +0 -10
- flwr/common/record/__init__.py +8 -4
- flwr/common/record/arrayrecord.py +626 -0
- flwr/common/record/{configsrecord.py → configrecord.py} +75 -29
- flwr/common/record/conversion_utils.py +9 -18
- flwr/common/record/{metricsrecord.py → metricrecord.py} +78 -32
- flwr/common/record/recorddict.py +288 -0
- flwr/common/recorddict_compat.py +410 -0
- flwr/common/secure_aggregation/quantization.py +5 -1
- flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
- flwr/common/serde.py +67 -190
- flwr/common/telemetry.py +0 -10
- flwr/common/typing.py +44 -8
- flwr/proto/exec_pb2.py +3 -3
- flwr/proto/exec_pb2.pyi +3 -3
- flwr/proto/message_pb2.py +12 -12
- flwr/proto/message_pb2.pyi +9 -9
- flwr/proto/recorddict_pb2.py +70 -0
- flwr/proto/{recordset_pb2.pyi → recorddict_pb2.pyi} +35 -35
- flwr/proto/run_pb2.py +31 -31
- flwr/proto/run_pb2.pyi +3 -3
- flwr/server/__init__.py +3 -1
- flwr/server/app.py +74 -3
- flwr/server/compat/__init__.py +2 -2
- flwr/server/compat/app.py +15 -12
- flwr/server/compat/app_utils.py +26 -18
- flwr/server/compat/{driver_client_proxy.py → grid_client_proxy.py} +41 -41
- flwr/server/fleet_event_log_interceptor.py +94 -0
- flwr/server/{driver → grid}/__init__.py +8 -7
- flwr/server/{driver/driver.py → grid/grid.py} +48 -19
- flwr/server/{driver/grpc_driver.py → grid/grpc_grid.py} +88 -56
- flwr/server/{driver/inmemory_driver.py → grid/inmemory_grid.py} +41 -54
- flwr/server/run_serverapp.py +6 -17
- flwr/server/server_app.py +126 -33
- flwr/server/serverapp/app.py +10 -10
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +2 -2
- flwr/server/superlink/fleet/message_handler/message_handler.py +8 -12
- flwr/server/superlink/fleet/vce/backend/backend.py +3 -3
- flwr/server/superlink/fleet/vce/backend/raybackend.py +2 -2
- flwr/server/superlink/fleet/vce/vce_api.py +33 -38
- flwr/server/superlink/linkstate/in_memory_linkstate.py +171 -132
- flwr/server/superlink/linkstate/linkstate.py +51 -64
- flwr/server/superlink/linkstate/sqlite_linkstate.py +253 -285
- flwr/server/superlink/linkstate/utils.py +171 -133
- flwr/server/superlink/{driver → serverappio}/__init__.py +1 -1
- flwr/server/superlink/{driver → serverappio}/serverappio_grpc.py +1 -1
- flwr/server/superlink/{driver → serverappio}/serverappio_servicer.py +27 -29
- flwr/server/superlink/simulation/simulationio_servicer.py +2 -2
- flwr/server/typing.py +3 -3
- flwr/server/utils/__init__.py +2 -2
- flwr/server/utils/validator.py +53 -68
- flwr/server/workflow/default_workflows.py +52 -58
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +48 -50
- flwr/simulation/app.py +2 -2
- flwr/simulation/ray_transport/ray_actor.py +4 -2
- flwr/simulation/ray_transport/ray_client_proxy.py +34 -32
- flwr/simulation/run_simulation.py +15 -15
- flwr/superexec/app.py +0 -14
- flwr/superexec/deployment.py +4 -4
- flwr/superexec/exec_event_log_interceptor.py +135 -0
- flwr/superexec/exec_grpc.py +10 -4
- flwr/superexec/exec_servicer.py +6 -6
- flwr/superexec/exec_user_auth_interceptor.py +22 -4
- flwr/superexec/executor.py +3 -3
- flwr/superexec/simulation.py +3 -3
- {flwr-1.15.2.dist-info → flwr-1.17.0.dist-info}/METADATA +5 -5
- {flwr-1.15.2.dist-info → flwr-1.17.0.dist-info}/RECORD +111 -112
- {flwr-1.15.2.dist-info → flwr-1.17.0.dist-info}/entry_points.txt +0 -3
- flwr/client/message_handler/task_handler.py +0 -37
- flwr/common/record/parametersrecord.py +0 -204
- flwr/common/record/recordset.py +0 -202
- flwr/common/recordset_compat.py +0 -418
- flwr/proto/recordset_pb2.py +0 -70
- flwr/proto/task_pb2.py +0 -33
- flwr/proto/task_pb2.pyi +0 -100
- flwr/proto/task_pb2_grpc.py +0 -4
- flwr/proto/task_pb2_grpc.pyi +0 -4
- /flwr/proto/{recordset_pb2_grpc.py → recorddict_pb2_grpc.py} +0 -0
- /flwr/proto/{recordset_pb2_grpc.pyi → recorddict_pb2_grpc.pyi} +0 -0
- {flwr-1.15.2.dist-info → flwr-1.17.0.dist-info}/LICENSE +0 -0
- {flwr-1.15.2.dist-info → flwr-1.17.0.dist-info}/WHEEL +0 -0
flwr/common/address.py
CHANGED
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
"""Flower IP address utils."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
import re
|
|
18
19
|
import socket
|
|
19
20
|
from ipaddress import ip_address
|
|
20
21
|
from typing import Optional
|
|
21
22
|
|
|
23
|
+
import grpc
|
|
24
|
+
|
|
22
25
|
IPV6: int = 6
|
|
23
26
|
|
|
24
27
|
|
|
@@ -101,3 +104,35 @@ def is_port_in_use(address: str) -> bool:
|
|
|
101
104
|
return True
|
|
102
105
|
|
|
103
106
|
return False
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def get_ip_address_from_servicer_context(context: grpc.ServicerContext) -> str:
|
|
110
|
+
"""Extract the client's IPv4 or IPv6 address from the gRPC ServicerContext.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
context : grpc.ServicerContext
|
|
115
|
+
The gRPC ServicerContext object. The context.peer() returns a string like
|
|
116
|
+
"ipv4:127.0.0.1:56789" for IPv4 and "ipv6:[2001:db8::1]:54321" for IPv6.
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
str
|
|
121
|
+
If one of the format matches, the function will return the client's IP address,
|
|
122
|
+
otherwise, it will raise a ValueError.
|
|
123
|
+
"""
|
|
124
|
+
peer: str = context.peer()
|
|
125
|
+
# Match IPv4: "ipv4:IP:port"
|
|
126
|
+
ipv4_match = re.match(r"^ipv4:(?P<ip>[^:]+):", peer)
|
|
127
|
+
if ipv4_match:
|
|
128
|
+
return ipv4_match.group("ip")
|
|
129
|
+
|
|
130
|
+
# Match IPv6: "ipv6:[IP]:port"
|
|
131
|
+
ipv6_match = re.match(r"^ipv6:\[(?P<ip>[^\]]+)\]:", peer)
|
|
132
|
+
if ipv6_match:
|
|
133
|
+
return ipv6_match.group("ip")
|
|
134
|
+
|
|
135
|
+
raise ValueError(
|
|
136
|
+
f"Unsupported peer address format: {peer} for the transport protocol. "
|
|
137
|
+
"The supported formats are ipv4:IP:port and ipv6:[IP]:port."
|
|
138
|
+
)
|
flwr/common/args.py
CHANGED
|
@@ -43,7 +43,8 @@ def add_args_flwr_app_common(parser: argparse.ArgumentParser) -> None:
|
|
|
43
43
|
"--insecure",
|
|
44
44
|
action="store_true",
|
|
45
45
|
help="Run the server without HTTPS, regardless of whether certificate "
|
|
46
|
-
"paths are provided.
|
|
46
|
+
"paths are provided. Data transmitted between the gRPC client and server "
|
|
47
|
+
"is not encrypted. By default, the server runs with HTTPS enabled. "
|
|
47
48
|
"Use this flag only if you understand the risks.",
|
|
48
49
|
)
|
|
49
50
|
|
|
@@ -99,7 +100,12 @@ def try_obtain_server_certificates(
|
|
|
99
100
|
) -> Optional[tuple[bytes, bytes, bytes]]:
|
|
100
101
|
"""Validate and return the CA cert, server cert, and server private key."""
|
|
101
102
|
if args.insecure:
|
|
102
|
-
log(
|
|
103
|
+
log(
|
|
104
|
+
WARN,
|
|
105
|
+
"Option `--insecure` was set. Starting insecure HTTP server with "
|
|
106
|
+
"unencrypted communication (TLS disabled). Proceed only if you understand "
|
|
107
|
+
"the risks.",
|
|
108
|
+
)
|
|
103
109
|
return None
|
|
104
110
|
# Check if certificates are provided
|
|
105
111
|
if args.ssl_certfile and args.ssl_keyfile and args.ssl_ca_certfile:
|
|
@@ -20,6 +20,7 @@ from collections.abc import Sequence
|
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from typing import Optional, Union
|
|
22
22
|
|
|
23
|
+
from flwr.common.typing import UserInfo
|
|
23
24
|
from flwr.proto.exec_pb2_grpc import ExecStub
|
|
24
25
|
|
|
25
26
|
from ..typing import UserAuthCredentials, UserAuthLoginDetails
|
|
@@ -49,7 +50,7 @@ class ExecAuthPlugin(ABC):
|
|
|
49
50
|
@abstractmethod
|
|
50
51
|
def validate_tokens_in_metadata(
|
|
51
52
|
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
|
52
|
-
) -> bool:
|
|
53
|
+
) -> tuple[bool, Optional[UserInfo]]:
|
|
53
54
|
"""Validate authentication tokens in the provided metadata."""
|
|
54
55
|
|
|
55
56
|
@abstractmethod
|
flwr/common/config.py
CHANGED
|
@@ -34,7 +34,7 @@ from flwr.common.constant import (
|
|
|
34
34
|
)
|
|
35
35
|
from flwr.common.typing import Run, UserConfig, UserConfigValue
|
|
36
36
|
|
|
37
|
-
from . import
|
|
37
|
+
from . import ConfigRecord, object_ref
|
|
38
38
|
|
|
39
39
|
T_dict = TypeVar("T_dict", bound=dict[str, Any]) # pylint: disable=invalid-name
|
|
40
40
|
|
|
@@ -260,9 +260,9 @@ def get_metadata_from_config(config: dict[str, Any]) -> tuple[str, str]:
|
|
|
260
260
|
)
|
|
261
261
|
|
|
262
262
|
|
|
263
|
-
def
|
|
264
|
-
"""Construct a `
|
|
265
|
-
c_record =
|
|
263
|
+
def user_config_to_configrecord(config: UserConfig) -> ConfigRecord:
|
|
264
|
+
"""Construct a `ConfigRecord` out of a `UserConfig`."""
|
|
265
|
+
c_record = ConfigRecord()
|
|
266
266
|
for k, v in config.items():
|
|
267
267
|
c_record[k] = v
|
|
268
268
|
|
flwr/common/constant.py
CHANGED
|
@@ -61,6 +61,7 @@ PING_CALL_TIMEOUT = 5
|
|
|
61
61
|
PING_BASE_MULTIPLIER = 0.8
|
|
62
62
|
PING_RANDOM_RANGE = (-0.1, 0.1)
|
|
63
63
|
PING_MAX_INTERVAL = 1e300
|
|
64
|
+
PING_PATIENCE = 2
|
|
64
65
|
|
|
65
66
|
# IDs
|
|
66
67
|
RUN_ID_NUM_BYTES = 8
|
|
@@ -120,6 +121,9 @@ TIMESTAMP_HEADER = "flwr-timestamp"
|
|
|
120
121
|
TIMESTAMP_TOLERANCE = 10 # General tolerance for timestamp verification
|
|
121
122
|
SYSTEM_TIME_TOLERANCE = 5 # Allowance for system time drift
|
|
122
123
|
|
|
124
|
+
# Constants for ArrayRecord
|
|
125
|
+
GC_THRESHOLD = 200_000_000 # 200 MB
|
|
126
|
+
|
|
123
127
|
|
|
124
128
|
class MessageType:
|
|
125
129
|
"""Message type."""
|
|
@@ -127,6 +131,7 @@ class MessageType:
|
|
|
127
131
|
TRAIN = "train"
|
|
128
132
|
EVALUATE = "evaluate"
|
|
129
133
|
QUERY = "query"
|
|
134
|
+
SYSTEM = "system"
|
|
130
135
|
|
|
131
136
|
def __new__(cls) -> MessageType:
|
|
132
137
|
"""Prevent instantiation."""
|
|
@@ -162,6 +167,7 @@ class ErrorCode:
|
|
|
162
167
|
CLIENT_APP_RAISED_EXCEPTION = 2
|
|
163
168
|
MESSAGE_UNAVAILABLE = 3
|
|
164
169
|
REPLY_MESSAGE_UNAVAILABLE = 4
|
|
170
|
+
NODE_UNAVAILABLE = 5
|
|
165
171
|
|
|
166
172
|
def __new__(cls) -> ErrorCode:
|
|
167
173
|
"""Prevent instantiation."""
|
|
@@ -212,3 +218,13 @@ class AuthType:
|
|
|
212
218
|
def __new__(cls) -> AuthType:
|
|
213
219
|
"""Prevent instantiation."""
|
|
214
220
|
raise TypeError(f"{cls.__name__} cannot be instantiated.")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class EventLogWriterType:
|
|
224
|
+
"""Event log writer types."""
|
|
225
|
+
|
|
226
|
+
STDOUT = "stdout"
|
|
227
|
+
|
|
228
|
+
def __new__(cls) -> EventLogWriterType:
|
|
229
|
+
"""Prevent instantiation."""
|
|
230
|
+
raise TypeError(f"{cls.__name__} cannot be instantiated.")
|
flwr/common/context.py
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
from dataclasses import dataclass
|
|
19
19
|
|
|
20
|
-
from .record import
|
|
20
|
+
from .record import RecordDict
|
|
21
21
|
from .typing import UserConfig
|
|
22
22
|
|
|
23
23
|
|
|
@@ -34,7 +34,7 @@ class Context:
|
|
|
34
34
|
node_config : UserConfig
|
|
35
35
|
A config (key/value mapping) unique to the node and independent of the
|
|
36
36
|
`run_config`. This config persists across all runs this node participates in.
|
|
37
|
-
state :
|
|
37
|
+
state : RecordDict
|
|
38
38
|
Holds records added by the entity in a given `run_id` and that will stay local.
|
|
39
39
|
This means that the data it holds will never leave the system it's running from.
|
|
40
40
|
This can be used as an intermediate storage or scratchpad when
|
|
@@ -50,7 +50,7 @@ class Context:
|
|
|
50
50
|
run_id: int
|
|
51
51
|
node_id: int
|
|
52
52
|
node_config: UserConfig
|
|
53
|
-
state:
|
|
53
|
+
state: RecordDict
|
|
54
54
|
run_config: UserConfig
|
|
55
55
|
|
|
56
56
|
def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
|
|
@@ -58,7 +58,7 @@ class Context:
|
|
|
58
58
|
run_id: int,
|
|
59
59
|
node_id: int,
|
|
60
60
|
node_config: UserConfig,
|
|
61
|
-
state:
|
|
61
|
+
state: RecordDict,
|
|
62
62
|
run_config: UserConfig,
|
|
63
63
|
) -> None:
|
|
64
64
|
self.run_id = run_id
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
"""Event log plugin components."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from .event_log_plugin import EventLogWriterPlugin as EventLogWriterPlugin
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"EventLogWriterPlugin",
|
|
22
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
"""Abstract class for Flower Event Log Writer Plugin."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from abc import ABC, abstractmethod
|
|
19
|
+
from typing import Optional, Union
|
|
20
|
+
|
|
21
|
+
import grpc
|
|
22
|
+
from google.protobuf.message import Message as GrpcMessage
|
|
23
|
+
|
|
24
|
+
from flwr.common.typing import LogEntry, UserInfo
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class EventLogWriterPlugin(ABC):
|
|
28
|
+
"""Abstract Flower Event Log Writer Plugin class for ExecServicer."""
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
"""Abstract constructor."""
|
|
33
|
+
|
|
34
|
+
@abstractmethod
|
|
35
|
+
def compose_log_before_event( # pylint: disable=too-many-arguments
|
|
36
|
+
self,
|
|
37
|
+
request: GrpcMessage,
|
|
38
|
+
context: grpc.ServicerContext,
|
|
39
|
+
user_info: Optional[UserInfo],
|
|
40
|
+
method_name: str,
|
|
41
|
+
) -> LogEntry:
|
|
42
|
+
"""Compose pre-event log entry from the provided request and context."""
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def compose_log_after_event( # pylint: disable=too-many-arguments,R0917
|
|
46
|
+
self,
|
|
47
|
+
request: GrpcMessage,
|
|
48
|
+
context: grpc.ServicerContext,
|
|
49
|
+
user_info: Optional[UserInfo],
|
|
50
|
+
method_name: str,
|
|
51
|
+
response: Optional[Union[GrpcMessage, BaseException]],
|
|
52
|
+
) -> LogEntry:
|
|
53
|
+
"""Compose post-event log entry from the provided response and context."""
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def write_log(
|
|
57
|
+
self,
|
|
58
|
+
log_entry: LogEntry,
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Write the event log to the specified data sink."""
|
flwr/common/grpc.py
CHANGED
|
@@ -27,7 +27,7 @@ import grpc
|
|
|
27
27
|
from .address import is_port_in_use
|
|
28
28
|
from .logger import log
|
|
29
29
|
|
|
30
|
-
GRPC_MAX_MESSAGE_LENGTH: int =
|
|
30
|
+
GRPC_MAX_MESSAGE_LENGTH: int = 2_147_483_647 # == 2048 * 1024 * 1024 -1 (2GB)
|
|
31
31
|
|
|
32
32
|
INVALID_CERTIFICATES_ERR_MSG = """
|
|
33
33
|
When setting any of root_certificate, certificate, or private_key,
|
flwr/common/logger.py
CHANGED
|
@@ -250,9 +250,9 @@ def warn_deprecated_feature_with_example(
|
|
|
250
250
|
log(
|
|
251
251
|
WARN,
|
|
252
252
|
"""FEATURE UPDATE: %s
|
|
253
|
-
|
|
253
|
+
------------------------------------------------------------
|
|
254
254
|
%s
|
|
255
|
-
|
|
255
|
+
------------------------------------------------------------
|
|
256
256
|
""",
|
|
257
257
|
example_message,
|
|
258
258
|
code_example,
|