flwr-nightly 1.19.0.dev20250516__py3-none-any.whl → 1.19.0.dev20250520__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/app/__init__.py +15 -0
- flwr/app/error.py +68 -0
- flwr/app/metadata.py +223 -0
- flwr/client/app.py +1 -1
- flwr/client/client_app.py +1 -1
- flwr/client/clientapp/app.py +1 -1
- flwr/client/grpc_rere_client/connection.py +2 -1
- flwr/client/rest_client/connection.py +2 -1
- flwr/clientapp/__init__.py +15 -0
- flwr/common/__init__.py +2 -2
- flwr/common/inflatable_grpc_utils.py +97 -0
- flwr/common/message.py +4 -243
- flwr/common/record/array.py +1 -1
- flwr/common/record/configrecord.py +1 -1
- flwr/common/serde.py +4 -1
- flwr/compat/__init__.py +15 -0
- flwr/compat/client/__init__.py +15 -0
- flwr/compat/common/__init__.py +15 -0
- flwr/compat/server/__init__.py +15 -0
- flwr/compat/simulation/__init__.py +15 -0
- flwr/server/superlink/fleet/vce/vce_api.py +1 -1
- flwr/serverapp/__init__.py +15 -0
- flwr/supercore/__init__.py +15 -0
- flwr/superlink/__init__.py +15 -0
- flwr/supernode/__init__.py +15 -0
- {flwr_nightly-1.19.0.dev20250516.dist-info → flwr_nightly-1.19.0.dev20250520.dist-info}/METADATA +1 -1
- {flwr_nightly-1.19.0.dev20250516.dist-info → flwr_nightly-1.19.0.dev20250520.dist-info}/RECORD +29 -15
- {flwr_nightly-1.19.0.dev20250516.dist-info → flwr_nightly-1.19.0.dev20250520.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.19.0.dev20250516.dist-info → flwr_nightly-1.19.0.dev20250520.dist-info}/entry_points.txt +0 -0
flwr/app/__init__.py
ADDED
@@ -0,0 +1,15 @@
|
|
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
|
+
"""Public Flower App APIs."""
|
flwr/app/error.py
ADDED
@@ -0,0 +1,68 @@
|
|
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
|
+
"""Error."""
|
16
|
+
|
17
|
+
|
18
|
+
from __future__ import annotations
|
19
|
+
|
20
|
+
from typing import Optional, cast
|
21
|
+
|
22
|
+
DEFAULT_TTL = 43200 # This is 12 hours
|
23
|
+
MESSAGE_INIT_ERROR_MESSAGE = (
|
24
|
+
"Invalid arguments for Message. Expected one of the documented "
|
25
|
+
"signatures: Message(content: RecordDict, dst_node_id: int, message_type: str,"
|
26
|
+
" *, [ttl: float, group_id: str]) or Message(content: RecordDict | error: Error,"
|
27
|
+
" *, reply_to: Message, [ttl: float])."
|
28
|
+
)
|
29
|
+
|
30
|
+
|
31
|
+
class Error:
|
32
|
+
"""The class storing information about an error that occurred.
|
33
|
+
|
34
|
+
Parameters
|
35
|
+
----------
|
36
|
+
code : int
|
37
|
+
An identifier for the error.
|
38
|
+
reason : Optional[str]
|
39
|
+
A reason for why the error arose (e.g. an exception stack-trace)
|
40
|
+
"""
|
41
|
+
|
42
|
+
def __init__(self, code: int, reason: str | None = None) -> None:
|
43
|
+
var_dict = {
|
44
|
+
"_code": code,
|
45
|
+
"_reason": reason,
|
46
|
+
}
|
47
|
+
self.__dict__.update(var_dict)
|
48
|
+
|
49
|
+
@property
|
50
|
+
def code(self) -> int:
|
51
|
+
"""Error code."""
|
52
|
+
return cast(int, self.__dict__["_code"])
|
53
|
+
|
54
|
+
@property
|
55
|
+
def reason(self) -> str | None:
|
56
|
+
"""Reason reported about the error."""
|
57
|
+
return cast(Optional[str], self.__dict__["_reason"])
|
58
|
+
|
59
|
+
def __repr__(self) -> str:
|
60
|
+
"""Return a string representation of this instance."""
|
61
|
+
view = ", ".join([f"{k.lstrip('_')}={v!r}" for k, v in self.__dict__.items()])
|
62
|
+
return f"{self.__class__.__qualname__}({view})"
|
63
|
+
|
64
|
+
def __eq__(self, other: object) -> bool:
|
65
|
+
"""Compare two instances of the class."""
|
66
|
+
if not isinstance(other, self.__class__):
|
67
|
+
raise NotImplementedError
|
68
|
+
return self.__dict__ == other.__dict__
|
flwr/app/metadata.py
ADDED
@@ -0,0 +1,223 @@
|
|
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
|
+
"""Metadata."""
|
16
|
+
|
17
|
+
|
18
|
+
from __future__ import annotations
|
19
|
+
|
20
|
+
from typing import cast
|
21
|
+
|
22
|
+
from ..common.constant import MessageType, MessageTypeLegacy
|
23
|
+
|
24
|
+
|
25
|
+
class Metadata: # pylint: disable=too-many-instance-attributes
|
26
|
+
"""The class representing metadata associated with the current message.
|
27
|
+
|
28
|
+
Parameters
|
29
|
+
----------
|
30
|
+
run_id : int
|
31
|
+
An identifier for the current run.
|
32
|
+
message_id : str
|
33
|
+
An identifier for the current message.
|
34
|
+
src_node_id : int
|
35
|
+
An identifier for the node sending this message.
|
36
|
+
dst_node_id : int
|
37
|
+
An identifier for the node receiving this message.
|
38
|
+
reply_to_message_id : str
|
39
|
+
An identifier for the message to which this message is a reply.
|
40
|
+
group_id : str
|
41
|
+
An identifier for grouping messages. In some settings,
|
42
|
+
this is used as the FL round.
|
43
|
+
created_at : float
|
44
|
+
Unix timestamp when the message was created.
|
45
|
+
ttl : float
|
46
|
+
Time-to-live for this message in seconds.
|
47
|
+
message_type : str
|
48
|
+
A string that encodes the action to be executed on
|
49
|
+
the receiving end.
|
50
|
+
"""
|
51
|
+
|
52
|
+
def __init__( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
53
|
+
self,
|
54
|
+
run_id: int,
|
55
|
+
message_id: str,
|
56
|
+
src_node_id: int,
|
57
|
+
dst_node_id: int,
|
58
|
+
reply_to_message_id: str,
|
59
|
+
group_id: str,
|
60
|
+
created_at: float,
|
61
|
+
ttl: float,
|
62
|
+
message_type: str,
|
63
|
+
) -> None:
|
64
|
+
var_dict = {
|
65
|
+
"_run_id": run_id,
|
66
|
+
"_message_id": message_id,
|
67
|
+
"_src_node_id": src_node_id,
|
68
|
+
"_dst_node_id": dst_node_id,
|
69
|
+
"_reply_to_message_id": reply_to_message_id,
|
70
|
+
"_group_id": group_id,
|
71
|
+
"_created_at": created_at,
|
72
|
+
"_ttl": ttl,
|
73
|
+
"_message_type": message_type,
|
74
|
+
}
|
75
|
+
self.__dict__.update(var_dict)
|
76
|
+
self.message_type = message_type # Trigger validation
|
77
|
+
|
78
|
+
@property
|
79
|
+
def run_id(self) -> int:
|
80
|
+
"""An identifier for the current run."""
|
81
|
+
return cast(int, self.__dict__["_run_id"])
|
82
|
+
|
83
|
+
@property
|
84
|
+
def message_id(self) -> str:
|
85
|
+
"""An identifier for the current message."""
|
86
|
+
return cast(str, self.__dict__["_message_id"])
|
87
|
+
|
88
|
+
@property
|
89
|
+
def src_node_id(self) -> int:
|
90
|
+
"""An identifier for the node sending this message."""
|
91
|
+
return cast(int, self.__dict__["_src_node_id"])
|
92
|
+
|
93
|
+
@property
|
94
|
+
def reply_to_message_id(self) -> str:
|
95
|
+
"""An identifier for the message to which this message is a reply."""
|
96
|
+
return cast(str, self.__dict__["_reply_to_message_id"])
|
97
|
+
|
98
|
+
@property
|
99
|
+
def dst_node_id(self) -> int:
|
100
|
+
"""An identifier for the node receiving this message."""
|
101
|
+
return cast(int, self.__dict__["_dst_node_id"])
|
102
|
+
|
103
|
+
@dst_node_id.setter
|
104
|
+
def dst_node_id(self, value: int) -> None:
|
105
|
+
"""Set dst_node_id."""
|
106
|
+
self.__dict__["_dst_node_id"] = value
|
107
|
+
|
108
|
+
@property
|
109
|
+
def group_id(self) -> str:
|
110
|
+
"""An identifier for grouping messages."""
|
111
|
+
return cast(str, self.__dict__["_group_id"])
|
112
|
+
|
113
|
+
@group_id.setter
|
114
|
+
def group_id(self, value: str) -> None:
|
115
|
+
"""Set group_id."""
|
116
|
+
self.__dict__["_group_id"] = value
|
117
|
+
|
118
|
+
@property
|
119
|
+
def created_at(self) -> float:
|
120
|
+
"""Unix timestamp when the message was created."""
|
121
|
+
return cast(float, self.__dict__["_created_at"])
|
122
|
+
|
123
|
+
@created_at.setter
|
124
|
+
def created_at(self, value: float) -> None:
|
125
|
+
"""Set creation timestamp of this message."""
|
126
|
+
self.__dict__["_created_at"] = value
|
127
|
+
|
128
|
+
@property
|
129
|
+
def delivered_at(self) -> str:
|
130
|
+
"""Unix timestamp when the message was delivered."""
|
131
|
+
return cast(str, self.__dict__["_delivered_at"])
|
132
|
+
|
133
|
+
@delivered_at.setter
|
134
|
+
def delivered_at(self, value: str) -> None:
|
135
|
+
"""Set delivery timestamp of this message."""
|
136
|
+
self.__dict__["_delivered_at"] = value
|
137
|
+
|
138
|
+
@property
|
139
|
+
def ttl(self) -> float:
|
140
|
+
"""Time-to-live for this message."""
|
141
|
+
return cast(float, self.__dict__["_ttl"])
|
142
|
+
|
143
|
+
@ttl.setter
|
144
|
+
def ttl(self, value: float) -> None:
|
145
|
+
"""Set ttl."""
|
146
|
+
self.__dict__["_ttl"] = value
|
147
|
+
|
148
|
+
@property
|
149
|
+
def message_type(self) -> str:
|
150
|
+
"""A string that encodes the action to be executed on the receiving end."""
|
151
|
+
return cast(str, self.__dict__["_message_type"])
|
152
|
+
|
153
|
+
@message_type.setter
|
154
|
+
def message_type(self, value: str) -> None:
|
155
|
+
"""Set message_type."""
|
156
|
+
# Validate message type
|
157
|
+
if validate_legacy_message_type(value):
|
158
|
+
pass # Backward compatibility for legacy message types
|
159
|
+
elif not validate_message_type(value):
|
160
|
+
raise ValueError(
|
161
|
+
f"Invalid message type: '{value}'. "
|
162
|
+
"Expected format: '<category>' or '<category>.<action>', "
|
163
|
+
"where <category> must be 'train', 'evaluate', or 'query', "
|
164
|
+
"and <action> must be a valid Python identifier."
|
165
|
+
)
|
166
|
+
|
167
|
+
self.__dict__["_message_type"] = value
|
168
|
+
|
169
|
+
def __repr__(self) -> str:
|
170
|
+
"""Return a string representation of this instance."""
|
171
|
+
view = ", ".join([f"{k.lstrip('_')}={v!r}" for k, v in self.__dict__.items()])
|
172
|
+
return f"{self.__class__.__qualname__}({view})"
|
173
|
+
|
174
|
+
def __eq__(self, other: object) -> bool:
|
175
|
+
"""Compare two instances of the class."""
|
176
|
+
if not isinstance(other, self.__class__):
|
177
|
+
raise NotImplementedError
|
178
|
+
return self.__dict__ == other.__dict__
|
179
|
+
|
180
|
+
|
181
|
+
def validate_message_type(message_type: str) -> bool:
|
182
|
+
"""Validate if the message type is valid.
|
183
|
+
|
184
|
+
A valid message type format must be one of the following:
|
185
|
+
|
186
|
+
- "<category>"
|
187
|
+
- "<category>.<action>"
|
188
|
+
|
189
|
+
where `category` must be one of "train", "evaluate", or "query",
|
190
|
+
and `action` must be a valid Python identifier.
|
191
|
+
"""
|
192
|
+
# Check if conforming to the format "<category>"
|
193
|
+
valid_types = {
|
194
|
+
MessageType.TRAIN,
|
195
|
+
MessageType.EVALUATE,
|
196
|
+
MessageType.QUERY,
|
197
|
+
MessageType.SYSTEM,
|
198
|
+
}
|
199
|
+
if message_type in valid_types:
|
200
|
+
return True
|
201
|
+
|
202
|
+
# Check if conforming to the format "<category>.<action>"
|
203
|
+
if message_type.count(".") != 1:
|
204
|
+
return False
|
205
|
+
|
206
|
+
category, action = message_type.split(".")
|
207
|
+
if category in valid_types and action.isidentifier():
|
208
|
+
return True
|
209
|
+
|
210
|
+
return False
|
211
|
+
|
212
|
+
|
213
|
+
def validate_legacy_message_type(message_type: str) -> bool:
|
214
|
+
"""Validate if the legacy message type is valid."""
|
215
|
+
# Backward compatibility for legacy message types
|
216
|
+
if message_type in (
|
217
|
+
MessageTypeLegacy.GET_PARAMETERS,
|
218
|
+
MessageTypeLegacy.GET_PROPERTIES,
|
219
|
+
"reconnect",
|
220
|
+
):
|
221
|
+
return True
|
222
|
+
|
223
|
+
return False
|
flwr/client/app.py
CHANGED
@@ -30,6 +30,7 @@ import grpc
|
|
30
30
|
from cryptography.hazmat.primitives.asymmetric import ec
|
31
31
|
from grpc import RpcError
|
32
32
|
|
33
|
+
from flwr.app.error import Error
|
33
34
|
from flwr.cli.config_utils import get_fab_metadata
|
34
35
|
from flwr.cli.install import install_from_fab
|
35
36
|
from flwr.client.client import Client
|
@@ -57,7 +58,6 @@ from flwr.common.constant import (
|
|
57
58
|
from flwr.common.exit import ExitCode, flwr_exit
|
58
59
|
from flwr.common.grpc import generic_create_grpc_server
|
59
60
|
from flwr.common.logger import log, warn_deprecated_feature
|
60
|
-
from flwr.common.message import Error
|
61
61
|
from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
|
62
62
|
from flwr.common.typing import Fab, Run, RunNotRunningException, UserConfig
|
63
63
|
from flwr.proto.clientappio_pb2_grpc import add_ClientAppIoServicer_to_server
|
flwr/client/client_app.py
CHANGED
@@ -20,6 +20,7 @@ from collections.abc import Iterator
|
|
20
20
|
from contextlib import contextmanager
|
21
21
|
from typing import Callable, Optional
|
22
22
|
|
23
|
+
from flwr.app.metadata import validate_message_type
|
23
24
|
from flwr.client.client import Client
|
24
25
|
from flwr.client.message_handler.message_handler import (
|
25
26
|
handle_legacy_message_from_msgtype,
|
@@ -28,7 +29,6 @@ from flwr.client.mod.utils import make_ffn
|
|
28
29
|
from flwr.client.typing import ClientFnExt, Mod
|
29
30
|
from flwr.common import Context, Message, MessageType
|
30
31
|
from flwr.common.logger import warn_deprecated_feature
|
31
|
-
from flwr.common.message import validate_message_type
|
32
32
|
|
33
33
|
from .typing import ClientAppCallable
|
34
34
|
|
flwr/client/clientapp/app.py
CHANGED
@@ -23,6 +23,7 @@ from typing import Optional
|
|
23
23
|
|
24
24
|
import grpc
|
25
25
|
|
26
|
+
from flwr.app.error import Error
|
26
27
|
from flwr.cli.install import install_from_fab
|
27
28
|
from flwr.client.client_app import ClientApp, LoadClientAppError
|
28
29
|
from flwr.common import Context, Message
|
@@ -32,7 +33,6 @@ from flwr.common.constant import CLIENTAPPIO_API_DEFAULT_CLIENT_ADDRESS, ErrorCo
|
|
32
33
|
from flwr.common.exit import ExitCode, flwr_exit
|
33
34
|
from flwr.common.grpc import create_channel, on_channel_state_change
|
34
35
|
from flwr.common.logger import log
|
35
|
-
from flwr.common.message import Error
|
36
36
|
from flwr.common.retry_invoker import _make_simple_grpc_retry_invoker, _wrap_stub
|
37
37
|
from flwr.common.serde import (
|
38
38
|
context_from_proto,
|
@@ -25,13 +25,14 @@ from typing import Callable, Optional, Union, cast
|
|
25
25
|
import grpc
|
26
26
|
from cryptography.hazmat.primitives.asymmetric import ec
|
27
27
|
|
28
|
+
from flwr.app.metadata import Metadata
|
28
29
|
from flwr.client.message_handler.message_handler import validate_out_message
|
29
30
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
30
31
|
from flwr.common.constant import HEARTBEAT_CALL_TIMEOUT, HEARTBEAT_DEFAULT_INTERVAL
|
31
32
|
from flwr.common.grpc import create_channel, on_channel_state_change
|
32
33
|
from flwr.common.heartbeat import HeartbeatSender
|
33
34
|
from flwr.common.logger import log
|
34
|
-
from flwr.common.message import Message
|
35
|
+
from flwr.common.message import Message
|
35
36
|
from flwr.common.retry_invoker import RetryInvoker
|
36
37
|
from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
|
37
38
|
generate_key_pairs,
|
@@ -25,13 +25,14 @@ from cryptography.hazmat.primitives.asymmetric import ec
|
|
25
25
|
from google.protobuf.message import Message as GrpcMessage
|
26
26
|
from requests.exceptions import ConnectionError as RequestsConnectionError
|
27
27
|
|
28
|
+
from flwr.app.metadata import Metadata
|
28
29
|
from flwr.client.message_handler.message_handler import validate_out_message
|
29
30
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
30
31
|
from flwr.common.constant import HEARTBEAT_DEFAULT_INTERVAL
|
31
32
|
from flwr.common.exit import ExitCode, flwr_exit
|
32
33
|
from flwr.common.heartbeat import HeartbeatSender
|
33
34
|
from flwr.common.logger import log
|
34
|
-
from flwr.common.message import Message
|
35
|
+
from flwr.common.message import Message
|
35
36
|
from flwr.common.retry_invoker import RetryInvoker
|
36
37
|
from flwr.common.serde import message_from_proto, message_to_proto, run_from_proto
|
37
38
|
from flwr.common.typing import Fab, Run
|
@@ -0,0 +1,15 @@
|
|
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
|
+
"""Public Flower ClientApp APIs."""
|
flwr/common/__init__.py
CHANGED
@@ -15,6 +15,8 @@
|
|
15
15
|
"""Common components shared between server and client."""
|
16
16
|
|
17
17
|
|
18
|
+
from ..app.error import Error as Error
|
19
|
+
from ..app.metadata import Metadata as Metadata
|
18
20
|
from .constant import MessageType as MessageType
|
19
21
|
from .constant import MessageTypeLegacy as MessageTypeLegacy
|
20
22
|
from .context import Context as Context
|
@@ -23,9 +25,7 @@ from .grpc import GRPC_MAX_MESSAGE_LENGTH
|
|
23
25
|
from .logger import configure as configure
|
24
26
|
from .logger import log as log
|
25
27
|
from .message import DEFAULT_TTL
|
26
|
-
from .message import Error as Error
|
27
28
|
from .message import Message as Message
|
28
|
-
from .message import Metadata as Metadata
|
29
29
|
from .parameter import bytes_to_ndarray as bytes_to_ndarray
|
30
30
|
from .parameter import ndarray_to_bytes as ndarray_to_bytes
|
31
31
|
from .parameter import ndarrays_to_parameters as ndarrays_to_parameters
|
@@ -0,0 +1,97 @@
|
|
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
|
+
"""InflatableObject utils."""
|
16
|
+
|
17
|
+
|
18
|
+
from typing import Union
|
19
|
+
|
20
|
+
from flwr.proto.fleet_pb2_grpc import FleetStub # pylint: disable=E0611
|
21
|
+
from flwr.proto.message_pb2 import ( # pylint: disable=E0611
|
22
|
+
PullObjectRequest,
|
23
|
+
PullObjectResponse,
|
24
|
+
PushObjectRequest,
|
25
|
+
PushObjectResponse,
|
26
|
+
)
|
27
|
+
from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub # pylint: disable=E0611
|
28
|
+
|
29
|
+
from .inflatable import (
|
30
|
+
InflatableObject,
|
31
|
+
get_object_head_values_from_object_content,
|
32
|
+
get_object_id,
|
33
|
+
)
|
34
|
+
from .record import Array, ArrayRecord, ConfigRecord, MetricRecord, RecordDict
|
35
|
+
|
36
|
+
# Helper registry that maps names of classes to their type
|
37
|
+
inflatable_class_registry: dict[str, type[InflatableObject]] = {
|
38
|
+
Array.__qualname__: Array,
|
39
|
+
ArrayRecord.__qualname__: ArrayRecord,
|
40
|
+
ConfigRecord.__qualname__: ConfigRecord,
|
41
|
+
MetricRecord.__qualname__: MetricRecord,
|
42
|
+
RecordDict.__qualname__: RecordDict,
|
43
|
+
}
|
44
|
+
|
45
|
+
|
46
|
+
def push_object_to_servicer(
|
47
|
+
obj: InflatableObject, stub: Union[FleetStub, ServerAppIoStub]
|
48
|
+
) -> set[str]:
|
49
|
+
"""Recursively deflate an object and push it to the servicer.
|
50
|
+
|
51
|
+
Objects with the same ID are not pushed twice. It returns the set of pushed object
|
52
|
+
IDs.
|
53
|
+
"""
|
54
|
+
pushed_object_ids: set[str] = set()
|
55
|
+
# Push children if it has any
|
56
|
+
if children := obj.children:
|
57
|
+
for child in children.values():
|
58
|
+
pushed_object_ids |= push_object_to_servicer(child, stub)
|
59
|
+
|
60
|
+
# Deflate object and push
|
61
|
+
object_content = obj.deflate()
|
62
|
+
object_id = get_object_id(object_content)
|
63
|
+
_: PushObjectResponse = stub.PushObject(
|
64
|
+
PushObjectRequest(
|
65
|
+
object_id=object_id,
|
66
|
+
object_content=object_content,
|
67
|
+
)
|
68
|
+
)
|
69
|
+
pushed_object_ids.add(object_id)
|
70
|
+
|
71
|
+
return pushed_object_ids
|
72
|
+
|
73
|
+
|
74
|
+
def pull_object_from_servicer(
|
75
|
+
object_id: str, stub: Union[FleetStub, ServerAppIoStub]
|
76
|
+
) -> InflatableObject:
|
77
|
+
"""Recursively inflate an object by pulling it from the servicer."""
|
78
|
+
# Pull object
|
79
|
+
object_proto: PullObjectResponse = stub.PullObject(
|
80
|
+
PullObjectRequest(object_id=object_id)
|
81
|
+
)
|
82
|
+
object_content = object_proto.object_content
|
83
|
+
|
84
|
+
# Extract object class and object_ids of children
|
85
|
+
obj_type, children_obj_ids, _ = get_object_head_values_from_object_content(
|
86
|
+
object_content=object_content
|
87
|
+
)
|
88
|
+
# Resolve object class
|
89
|
+
cls_type = inflatable_class_registry[obj_type]
|
90
|
+
|
91
|
+
# Pull all children objects
|
92
|
+
children: dict[str, InflatableObject] = {}
|
93
|
+
for child_object_id in children_obj_ids:
|
94
|
+
children[child_object_id] = pull_object_from_servicer(child_object_id, stub)
|
95
|
+
|
96
|
+
# Inflate object passing its children
|
97
|
+
return cls_type.inflate(object_content, children=children)
|
flwr/common/message.py
CHANGED
@@ -18,12 +18,14 @@
|
|
18
18
|
from __future__ import annotations
|
19
19
|
|
20
20
|
from logging import WARNING
|
21
|
-
from typing import Any,
|
21
|
+
from typing import Any, cast, overload
|
22
22
|
|
23
23
|
from flwr.common.date import now
|
24
24
|
from flwr.common.logger import warn_deprecated_feature
|
25
25
|
|
26
|
-
from .
|
26
|
+
from ..app.error import Error
|
27
|
+
from ..app.metadata import Metadata
|
28
|
+
from .constant import MESSAGE_TTL_TOLERANCE
|
27
29
|
from .logger import log
|
28
30
|
from .record import RecordDict
|
29
31
|
|
@@ -56,202 +58,6 @@ class MessageInitializationError(TypeError):
|
|
56
58
|
super().__init__(message or MESSAGE_INIT_ERROR_MESSAGE)
|
57
59
|
|
58
60
|
|
59
|
-
class Metadata: # pylint: disable=too-many-instance-attributes
|
60
|
-
"""The class representing metadata associated with the current message.
|
61
|
-
|
62
|
-
Parameters
|
63
|
-
----------
|
64
|
-
run_id : int
|
65
|
-
An identifier for the current run.
|
66
|
-
message_id : str
|
67
|
-
An identifier for the current message.
|
68
|
-
src_node_id : int
|
69
|
-
An identifier for the node sending this message.
|
70
|
-
dst_node_id : int
|
71
|
-
An identifier for the node receiving this message.
|
72
|
-
reply_to_message_id : str
|
73
|
-
An identifier for the message to which this message is a reply.
|
74
|
-
group_id : str
|
75
|
-
An identifier for grouping messages. In some settings,
|
76
|
-
this is used as the FL round.
|
77
|
-
created_at : float
|
78
|
-
Unix timestamp when the message was created.
|
79
|
-
ttl : float
|
80
|
-
Time-to-live for this message in seconds.
|
81
|
-
message_type : str
|
82
|
-
A string that encodes the action to be executed on
|
83
|
-
the receiving end.
|
84
|
-
"""
|
85
|
-
|
86
|
-
def __init__( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
87
|
-
self,
|
88
|
-
run_id: int,
|
89
|
-
message_id: str,
|
90
|
-
src_node_id: int,
|
91
|
-
dst_node_id: int,
|
92
|
-
reply_to_message_id: str,
|
93
|
-
group_id: str,
|
94
|
-
created_at: float,
|
95
|
-
ttl: float,
|
96
|
-
message_type: str,
|
97
|
-
) -> None:
|
98
|
-
var_dict = {
|
99
|
-
"_run_id": run_id,
|
100
|
-
"_message_id": message_id,
|
101
|
-
"_src_node_id": src_node_id,
|
102
|
-
"_dst_node_id": dst_node_id,
|
103
|
-
"_reply_to_message_id": reply_to_message_id,
|
104
|
-
"_group_id": group_id,
|
105
|
-
"_created_at": created_at,
|
106
|
-
"_ttl": ttl,
|
107
|
-
"_message_type": message_type,
|
108
|
-
}
|
109
|
-
self.__dict__.update(var_dict)
|
110
|
-
self.message_type = message_type # Trigger validation
|
111
|
-
|
112
|
-
@property
|
113
|
-
def run_id(self) -> int:
|
114
|
-
"""An identifier for the current run."""
|
115
|
-
return cast(int, self.__dict__["_run_id"])
|
116
|
-
|
117
|
-
@property
|
118
|
-
def message_id(self) -> str:
|
119
|
-
"""An identifier for the current message."""
|
120
|
-
return cast(str, self.__dict__["_message_id"])
|
121
|
-
|
122
|
-
@property
|
123
|
-
def src_node_id(self) -> int:
|
124
|
-
"""An identifier for the node sending this message."""
|
125
|
-
return cast(int, self.__dict__["_src_node_id"])
|
126
|
-
|
127
|
-
@property
|
128
|
-
def reply_to_message_id(self) -> str:
|
129
|
-
"""An identifier for the message to which this message is a reply."""
|
130
|
-
return cast(str, self.__dict__["_reply_to_message_id"])
|
131
|
-
|
132
|
-
@property
|
133
|
-
def dst_node_id(self) -> int:
|
134
|
-
"""An identifier for the node receiving this message."""
|
135
|
-
return cast(int, self.__dict__["_dst_node_id"])
|
136
|
-
|
137
|
-
@dst_node_id.setter
|
138
|
-
def dst_node_id(self, value: int) -> None:
|
139
|
-
"""Set dst_node_id."""
|
140
|
-
self.__dict__["_dst_node_id"] = value
|
141
|
-
|
142
|
-
@property
|
143
|
-
def group_id(self) -> str:
|
144
|
-
"""An identifier for grouping messages."""
|
145
|
-
return cast(str, self.__dict__["_group_id"])
|
146
|
-
|
147
|
-
@group_id.setter
|
148
|
-
def group_id(self, value: str) -> None:
|
149
|
-
"""Set group_id."""
|
150
|
-
self.__dict__["_group_id"] = value
|
151
|
-
|
152
|
-
@property
|
153
|
-
def created_at(self) -> float:
|
154
|
-
"""Unix timestamp when the message was created."""
|
155
|
-
return cast(float, self.__dict__["_created_at"])
|
156
|
-
|
157
|
-
@created_at.setter
|
158
|
-
def created_at(self, value: float) -> None:
|
159
|
-
"""Set creation timestamp of this message."""
|
160
|
-
self.__dict__["_created_at"] = value
|
161
|
-
|
162
|
-
@property
|
163
|
-
def delivered_at(self) -> str:
|
164
|
-
"""Unix timestamp when the message was delivered."""
|
165
|
-
return cast(str, self.__dict__["_delivered_at"])
|
166
|
-
|
167
|
-
@delivered_at.setter
|
168
|
-
def delivered_at(self, value: str) -> None:
|
169
|
-
"""Set delivery timestamp of this message."""
|
170
|
-
self.__dict__["_delivered_at"] = value
|
171
|
-
|
172
|
-
@property
|
173
|
-
def ttl(self) -> float:
|
174
|
-
"""Time-to-live for this message."""
|
175
|
-
return cast(float, self.__dict__["_ttl"])
|
176
|
-
|
177
|
-
@ttl.setter
|
178
|
-
def ttl(self, value: float) -> None:
|
179
|
-
"""Set ttl."""
|
180
|
-
self.__dict__["_ttl"] = value
|
181
|
-
|
182
|
-
@property
|
183
|
-
def message_type(self) -> str:
|
184
|
-
"""A string that encodes the action to be executed on the receiving end."""
|
185
|
-
return cast(str, self.__dict__["_message_type"])
|
186
|
-
|
187
|
-
@message_type.setter
|
188
|
-
def message_type(self, value: str) -> None:
|
189
|
-
"""Set message_type."""
|
190
|
-
# Validate message type
|
191
|
-
if validate_legacy_message_type(value):
|
192
|
-
pass # Backward compatibility for legacy message types
|
193
|
-
elif not validate_message_type(value):
|
194
|
-
raise ValueError(
|
195
|
-
f"Invalid message type: '{value}'. "
|
196
|
-
"Expected format: '<category>' or '<category>.<action>', "
|
197
|
-
"where <category> must be 'train', 'evaluate', or 'query', "
|
198
|
-
"and <action> must be a valid Python identifier."
|
199
|
-
)
|
200
|
-
|
201
|
-
self.__dict__["_message_type"] = value
|
202
|
-
|
203
|
-
def __repr__(self) -> str:
|
204
|
-
"""Return a string representation of this instance."""
|
205
|
-
view = ", ".join([f"{k.lstrip('_')}={v!r}" for k, v in self.__dict__.items()])
|
206
|
-
return f"{self.__class__.__qualname__}({view})"
|
207
|
-
|
208
|
-
def __eq__(self, other: object) -> bool:
|
209
|
-
"""Compare two instances of the class."""
|
210
|
-
if not isinstance(other, self.__class__):
|
211
|
-
raise NotImplementedError
|
212
|
-
return self.__dict__ == other.__dict__
|
213
|
-
|
214
|
-
|
215
|
-
class Error:
|
216
|
-
"""The class storing information about an error that occurred.
|
217
|
-
|
218
|
-
Parameters
|
219
|
-
----------
|
220
|
-
code : int
|
221
|
-
An identifier for the error.
|
222
|
-
reason : Optional[str]
|
223
|
-
A reason for why the error arose (e.g. an exception stack-trace)
|
224
|
-
"""
|
225
|
-
|
226
|
-
def __init__(self, code: int, reason: str | None = None) -> None:
|
227
|
-
var_dict = {
|
228
|
-
"_code": code,
|
229
|
-
"_reason": reason,
|
230
|
-
}
|
231
|
-
self.__dict__.update(var_dict)
|
232
|
-
|
233
|
-
@property
|
234
|
-
def code(self) -> int:
|
235
|
-
"""Error code."""
|
236
|
-
return cast(int, self.__dict__["_code"])
|
237
|
-
|
238
|
-
@property
|
239
|
-
def reason(self) -> str | None:
|
240
|
-
"""Reason reported about the error."""
|
241
|
-
return cast(Optional[str], self.__dict__["_reason"])
|
242
|
-
|
243
|
-
def __repr__(self) -> str:
|
244
|
-
"""Return a string representation of this instance."""
|
245
|
-
view = ", ".join([f"{k.lstrip('_')}={v!r}" for k, v in self.__dict__.items()])
|
246
|
-
return f"{self.__class__.__qualname__}({view})"
|
247
|
-
|
248
|
-
def __eq__(self, other: object) -> bool:
|
249
|
-
"""Compare two instances of the class."""
|
250
|
-
if not isinstance(other, self.__class__):
|
251
|
-
raise NotImplementedError
|
252
|
-
return self.__dict__ == other.__dict__
|
253
|
-
|
254
|
-
|
255
61
|
class Message:
|
256
62
|
"""Represents a message exchanged between ClientApp and ServerApp.
|
257
63
|
|
@@ -614,48 +420,3 @@ def _check_arg_types( # pylint: disable=too-many-arguments, R0917
|
|
614
420
|
):
|
615
421
|
return
|
616
422
|
raise MessageInitializationError()
|
617
|
-
|
618
|
-
|
619
|
-
def validate_message_type(message_type: str) -> bool:
|
620
|
-
"""Validate if the message type is valid.
|
621
|
-
|
622
|
-
A valid message type format must be one of the following:
|
623
|
-
|
624
|
-
- "<category>"
|
625
|
-
- "<category>.<action>"
|
626
|
-
|
627
|
-
where `category` must be one of "train", "evaluate", or "query",
|
628
|
-
and `action` must be a valid Python identifier.
|
629
|
-
"""
|
630
|
-
# Check if conforming to the format "<category>"
|
631
|
-
valid_types = {
|
632
|
-
MessageType.TRAIN,
|
633
|
-
MessageType.EVALUATE,
|
634
|
-
MessageType.QUERY,
|
635
|
-
MessageType.SYSTEM,
|
636
|
-
}
|
637
|
-
if message_type in valid_types:
|
638
|
-
return True
|
639
|
-
|
640
|
-
# Check if conforming to the format "<category>.<action>"
|
641
|
-
if message_type.count(".") != 1:
|
642
|
-
return False
|
643
|
-
|
644
|
-
category, action = message_type.split(".")
|
645
|
-
if category in valid_types and action.isidentifier():
|
646
|
-
return True
|
647
|
-
|
648
|
-
return False
|
649
|
-
|
650
|
-
|
651
|
-
def validate_legacy_message_type(message_type: str) -> bool:
|
652
|
-
"""Validate if the legacy message type is valid."""
|
653
|
-
# Backward compatibility for legacy message types
|
654
|
-
if message_type in (
|
655
|
-
MessageTypeLegacy.GET_PARAMETERS,
|
656
|
-
MessageTypeLegacy.GET_PROPERTIES,
|
657
|
-
"reconnect",
|
658
|
-
):
|
659
|
-
return True
|
660
|
-
|
661
|
-
return False
|
flwr/common/record/array.py
CHANGED
@@ -204,7 +204,7 @@ class ConfigRecord(TypedDict[str, ConfigRecordValues], InflatableObject):
|
|
204
204
|
ConfigRecord
|
205
205
|
The inflated ConfigRecord.
|
206
206
|
"""
|
207
|
-
if children
|
207
|
+
if children:
|
208
208
|
raise ValueError("`ConfigRecord` objects do not have children.")
|
209
209
|
|
210
210
|
obj_body = get_object_body(object_content, cls)
|
flwr/common/serde.py
CHANGED
@@ -44,6 +44,9 @@ from flwr.proto.transport_pb2 import (
|
|
44
44
|
Status,
|
45
45
|
)
|
46
46
|
|
47
|
+
from ..app.error import Error
|
48
|
+
from ..app.metadata import Metadata
|
49
|
+
|
47
50
|
# pylint: enable=E0611
|
48
51
|
from . import (
|
49
52
|
Array,
|
@@ -55,7 +58,7 @@ from . import (
|
|
55
58
|
typing,
|
56
59
|
)
|
57
60
|
from .constant import INT64_MAX_VALUE
|
58
|
-
from .message import
|
61
|
+
from .message import Message, make_message
|
59
62
|
from .serde_utils import record_value_dict_from_proto, record_value_dict_to_proto
|
60
63
|
|
61
64
|
# === Parameters message ===
|
flwr/compat/__init__.py
ADDED
@@ -0,0 +1,15 @@
|
|
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
|
+
"""Compatibility package containing deprecated legacy components."""
|
@@ -0,0 +1,15 @@
|
|
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
|
+
"""Legacy components previously located in ``flwr.client``."""
|
@@ -0,0 +1,15 @@
|
|
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
|
+
"""Legacy components previously located in ``flwr.common``."""
|
@@ -0,0 +1,15 @@
|
|
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
|
+
"""Legacy components previously located in ``flwr.server``."""
|
@@ -0,0 +1,15 @@
|
|
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
|
+
"""Legacy components previously located in ``flwr.simulation``."""
|
@@ -26,6 +26,7 @@ from queue import Empty, Queue
|
|
26
26
|
from time import sleep
|
27
27
|
from typing import Callable, Optional
|
28
28
|
|
29
|
+
from flwr.app.error import Error
|
29
30
|
from flwr.client.client_app import ClientApp, ClientAppException, LoadClientAppError
|
30
31
|
from flwr.client.clientapp.utils import get_load_client_app_fn
|
31
32
|
from flwr.client.run_info_store import DeprecatedRunInfoStore
|
@@ -37,7 +38,6 @@ from flwr.common.constant import (
|
|
37
38
|
ErrorCode,
|
38
39
|
)
|
39
40
|
from flwr.common.logger import log
|
40
|
-
from flwr.common.message import Error
|
41
41
|
from flwr.common.typing import Run
|
42
42
|
from flwr.server.superlink.linkstate import LinkState, LinkStateFactory
|
43
43
|
|
@@ -0,0 +1,15 @@
|
|
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
|
+
"""Public Flower ServerApp APIs."""
|
@@ -0,0 +1,15 @@
|
|
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
|
+
"""Infrastructure components shared between SuperLink and SuperNode."""
|
@@ -0,0 +1,15 @@
|
|
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 SuperLink."""
|
@@ -0,0 +1,15 @@
|
|
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 SuperNode."""
|
{flwr_nightly-1.19.0.dev20250516.dist-info → flwr_nightly-1.19.0.dev20250520.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: flwr-nightly
|
3
|
-
Version: 1.19.0.
|
3
|
+
Version: 1.19.0.dev20250520
|
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
|
{flwr_nightly-1.19.0.dev20250516.dist-info → flwr_nightly-1.19.0.dev20250520.dist-info}/RECORD
RENAMED
@@ -1,4 +1,7 @@
|
|
1
1
|
flwr/__init__.py,sha256=5JdNd_I_ZxHJv2tbnM_Ug2_LQ4DkhZ2FiA7l23V13hU,937
|
2
|
+
flwr/app/__init__.py,sha256=VNahoTMYIbIQt8EMit-UvliLoJib7uSsvKANJJXUWzM,713
|
3
|
+
flwr/app/error.py,sha256=0PwA-E_CAs5P_nWAA0kksVO1A44t4CNLEf7u-Su-uJ0,2342
|
4
|
+
flwr/app/metadata.py,sha256=rdMBn0zhIOYmCvmGENQWSQqDwcxwsMJzCle4PQdlc_Y,7331
|
2
5
|
flwr/cli/__init__.py,sha256=EfMGmHoobET6P2blBt_eOByXL8299MgFfB7XNdaPQ6I,720
|
3
6
|
flwr/cli/app.py,sha256=AKCP45Dkbpvdil_4Ir9S93L3HP3iUOnHmcZjscoM8uU,1856
|
4
7
|
flwr/cli/auth_plugin/__init__.py,sha256=FyaoqPzcxlBTFfJ2sBRC5USwQLmAhFr5KuBwfMO4bmo,1052
|
@@ -72,11 +75,11 @@ flwr/cli/run/run.py,sha256=mbyf46Tm3qrL8NW02JyDjs6BI49m9UMzXsGK8-Af1r4,8232
|
|
72
75
|
flwr/cli/stop.py,sha256=iLbh1dq8XMdcIlh0Lh8ufG6h0VvrP1kyp_mGO-kimt0,4976
|
73
76
|
flwr/cli/utils.py,sha256=FjRYfzTw75qh5YHmrg9XzBA6o73T6xWt9WQYIxq-iHY,11207
|
74
77
|
flwr/client/__init__.py,sha256=FslaZOoCGPIzlK-NhL7bFMVVnmFDOh_PhW4AfGzno68,1192
|
75
|
-
flwr/client/app.py,sha256=
|
78
|
+
flwr/client/app.py,sha256=wTZHxy8FS_lcIP3uXc6IzsDpbeDg0arWJGckCy3Xcn4,34277
|
76
79
|
flwr/client/client.py,sha256=3HAchxvknKG9jYbB7swNyDj-e5vUWDuMKoLvbT7jCVM,7895
|
77
|
-
flwr/client/client_app.py,sha256=
|
80
|
+
flwr/client/client_app.py,sha256=zVhi-l3chAb06ozFsKwix3hU_RpOLjST13Ha50AVIPE,16918
|
78
81
|
flwr/client/clientapp/__init__.py,sha256=nPMoWEB1FhwexuW-vKdhwFkFr_4MW-2YMZExP9vfTGg,800
|
79
|
-
flwr/client/clientapp/app.py,sha256=
|
82
|
+
flwr/client/clientapp/app.py,sha256=N1nd4PnwWzzZc3kn1g01SULXVMriCstCnfDYV_KERqc,9057
|
80
83
|
flwr/client/clientapp/clientappio_servicer.py,sha256=LmzkxtNQBn5vVrHc0Bhq2WqaK6-LM2v4kfLBN0PiNNM,8522
|
81
84
|
flwr/client/clientapp/utils.py,sha256=LsiW1OL2VPcjom3xN29pgBQC0UrttQ-xWL_GF1fkKDo,4344
|
82
85
|
flwr/client/dpfedavg_numpy_client.py,sha256=3hul067cT2E9jBhzp7bFnFAZ_D2nWcIUEdHYE05FpzU,7404
|
@@ -86,7 +89,7 @@ flwr/client/grpc_client/__init__.py,sha256=MDOckOODn-FJnkkFEfb2JO-2G97wrBr_TTqht
|
|
86
89
|
flwr/client/grpc_client/connection.py,sha256=xAyvcTVr7bkwUfR5P3D_LKlZYiyySpt5sEwORA1h8Gc,9189
|
87
90
|
flwr/client/grpc_rere_client/__init__.py,sha256=i7iS0Lt8B7q0E2L72e4F_YrKm6ClRKnd71PNA6PW2O0,752
|
88
91
|
flwr/client/grpc_rere_client/client_interceptor.py,sha256=zFaVHw6AxeNO-7eCKKb-RxrPa7zbM5Z-2-1Efc4adQY,2451
|
89
|
-
flwr/client/grpc_rere_client/connection.py,sha256=
|
92
|
+
flwr/client/grpc_rere_client/connection.py,sha256=MCUkpx2KGETTPOrBGZfp2rktMOHmuPR4x0eqcevZapo,12064
|
90
93
|
flwr/client/grpc_rere_client/grpc_adapter.py,sha256=JvMZ7vCFTaTEo6AzKYh3zDmeQAU7VSjdysbC6t3ufWg,6351
|
91
94
|
flwr/client/message_handler/__init__.py,sha256=0lyljDVqre3WljiZbPcwCCf8GiIaSVI_yo_ylEyPwSE,719
|
92
95
|
flwr/client/message_handler/message_handler.py,sha256=-vZKGg2gP81182LFXDmiZtajLlIfZjV6FyMS43qQVwU,6532
|
@@ -104,12 +107,13 @@ flwr/client/nodestate/nodestate.py,sha256=-LAjZOnS7VyHC05ll3b31cYDjwAt6l4WmYt7du
|
|
104
107
|
flwr/client/nodestate/nodestate_factory.py,sha256=UYTDCcwK_baHUmkzkJDxL0UEqvtTfOMlQRrROMCd0Xo,1430
|
105
108
|
flwr/client/numpy_client.py,sha256=Qq6ghsIAop2slKqAfgiI5NiHJ4LIxGmrik3Ror4_XVc,9581
|
106
109
|
flwr/client/rest_client/__init__.py,sha256=MBiuK62hj439m9rtwSwI184Hth6Tt5GbmpNMyl3zkZY,735
|
107
|
-
flwr/client/rest_client/connection.py,sha256=
|
110
|
+
flwr/client/rest_client/connection.py,sha256=Xlf1eEMXq17VVVELPGPT1pqJKw8l0iq4Jnvz13v95C8,12806
|
108
111
|
flwr/client/run_info_store.py,sha256=MaJ3UQ-07hWtK67wnWu0zR29jrk0fsfgJX506dvEOfE,4042
|
109
112
|
flwr/client/supernode/__init__.py,sha256=i3gFbV5ie_FGyRMpzOvqtZAi0Z0ChIEJ7I2Kr0ym0PM,793
|
110
113
|
flwr/client/supernode/app.py,sha256=lURLjP8jiOWhlX3-uh-7t_l1o_JEUz_FmkuNY91xmUQ,8975
|
111
114
|
flwr/client/typing.py,sha256=Jw3rawDzI_-ZDcRmEQcs5gZModY7oeQlEeltYsdOhlU,1048
|
112
|
-
flwr/
|
115
|
+
flwr/clientapp/__init__.py,sha256=zGW4z49Ojzoi1hDiRC7kyhLjijUilc6fqHhtM_ATRVA,719
|
116
|
+
flwr/common/__init__.py,sha256=5GCLVk399Az_rTJHNticRlL0Sl_oPw_j5_LuFKfX7-M,4171
|
113
117
|
flwr/common/address.py,sha256=9JucdTwlc-jpeJkRKeUboZoacUtErwSVtnDR9kAtLqE,4119
|
114
118
|
flwr/common/args.py,sha256=-aX_jVnSaDrJR2KZ8Wq0Y3dQHII4R4MJtJOIXzVUA0c,5417
|
115
119
|
flwr/common/auth_plugin/__init__.py,sha256=m271m9YjK2QfKDOuIIhcTvGmv1GWh1PL97QB05NTSHs,887
|
@@ -130,15 +134,16 @@ flwr/common/exit_handlers.py,sha256=MEk5_savTLphn-6lW57UQlos-XrFA39XEBn-OF1vXXg,
|
|
130
134
|
flwr/common/grpc.py,sha256=manTaHaPiyYngUq1ErZvvV2B2GxlXUUUGRy3jc3TBIQ,9798
|
131
135
|
flwr/common/heartbeat.py,sha256=SyEpNDnmJ0lni0cWO67rcoJVKasCLmkNHm3dKLeNrLU,5749
|
132
136
|
flwr/common/inflatable.py,sha256=ZKW4L2GMAxInUlbNK_zDZs7uW4-CuQui9TnWVglpDic,5279
|
137
|
+
flwr/common/inflatable_grpc_utils.py,sha256=StkhGH8x9zR-p5MH52HdLG9MLzKv_rT8sPdbR9ZzNyE,3368
|
133
138
|
flwr/common/logger.py,sha256=JbRf6E2vQxXzpDBq1T8IDUJo_usu3gjWEBPQ6uKcmdg,13049
|
134
|
-
flwr/common/message.py,sha256=
|
139
|
+
flwr/common/message.py,sha256=4QPeMFP_BWPltgi0-0ARROhtL2k1b4BFflrgWCn3uJ0,16020
|
135
140
|
flwr/common/object_ref.py,sha256=p3SfTeqo3Aj16SkB-vsnNn01zswOPdGNBitcbRnqmUk,9134
|
136
141
|
flwr/common/parameter.py,sha256=UVw6sOgehEFhFs4uUCMl2kfVq1PD6ncmWgPLMsZPKPE,2095
|
137
142
|
flwr/common/pyproject.py,sha256=2SU6yJW7059SbMXgzjOdK1GZRWO6AixDH7BmdxbMvHI,1386
|
138
143
|
flwr/common/record/__init__.py,sha256=cNGccdDoxttqgnUgyKRIqLWULjW-NaSmOufVxtXq-sw,1197
|
139
|
-
flwr/common/record/array.py,sha256=
|
144
|
+
flwr/common/record/array.py,sha256=XQAXLFXc_dXkwrbrPzXqqVPLW_O1JLIuyrOtAmbmIKY,10838
|
140
145
|
flwr/common/record/arrayrecord.py,sha256=1DII6iloauHvBaWPzYtgaVAT9plNRBaaInGA6p8-j20,16787
|
141
|
-
flwr/common/record/configrecord.py,sha256=
|
146
|
+
flwr/common/record/configrecord.py,sha256=nDoIc_-vh7XUx2BuojenpcqSgM2XxD4NyGFAYpmXabM,9652
|
142
147
|
flwr/common/record/conversion_utils.py,sha256=wbNCzy7oAqaA3-arhls_EqRZYXRC4YrWIoE-Gy82fJ0,1191
|
143
148
|
flwr/common/record/metricrecord.py,sha256=Gxl9TdVpMAHg6pNN2SxB-as8iPDnPx398KEhORU4n3A,8839
|
144
149
|
flwr/common/record/recorddict.py,sha256=p7hBimFpKM1XKUe6OAkR_7pYGzGL_EwUJUvJ8odZEcY,14986
|
@@ -153,11 +158,16 @@ flwr/common/secure_aggregation/ndarrays_arithmetic.py,sha256=TrggOlizlny3V2KS7-3
|
|
153
158
|
flwr/common/secure_aggregation/quantization.py,sha256=ssFZpiRyj9ltIh0Ai3vGkDqWFO4SoqgoD1mDU9XqMEM,2400
|
154
159
|
flwr/common/secure_aggregation/secaggplus_constants.py,sha256=dGYhWOBMMDJcQH4_tQNC8-Efqm-ecEUNN9ANz59UnCk,2182
|
155
160
|
flwr/common/secure_aggregation/secaggplus_utils.py,sha256=E_xU-Zd45daO1em7M6C2wOjFXVtJf-6tl7fp-7xq1wo,3214
|
156
|
-
flwr/common/serde.py,sha256=
|
161
|
+
flwr/common/serde.py,sha256=IMU32jIwcF_iPej7d8PXLxZ4xZxFrsy4XPdlDyXQ9bk,23960
|
157
162
|
flwr/common/serde_utils.py,sha256=ofmrgVHRBfrE1MtQwLQk0x12JS9vL-u8wHXrgZE2ueg,3985
|
158
163
|
flwr/common/telemetry.py,sha256=jF47v0SbnBd43XamHtl3wKxs3knFUY2p77cm_2lzZ8M,8762
|
159
164
|
flwr/common/typing.py,sha256=97QRfRRS7sQnjkAI5FDZ01-38oQUSz4i1qqewQmBWRg,6886
|
160
165
|
flwr/common/version.py,sha256=7GAGzPn73Mkh09qhrjbmjZQtcqVhBuzhFBaK4Mk4VRk,1325
|
166
|
+
flwr/compat/__init__.py,sha256=gbfDQKKKMZzi3GswyVRgyLdDlHiWj3wU6dg7y6m5O_s,752
|
167
|
+
flwr/compat/client/__init__.py,sha256=qpbo0lcxdNL4qy5KHqiGm8OLxSxkYgI_-dLh5rwhtcI,746
|
168
|
+
flwr/compat/common/__init__.py,sha256=OMnKw4ad0qYMSIA9LZRa2gOkhSOXwAZCpAHnBQE_hFc,746
|
169
|
+
flwr/compat/server/__init__.py,sha256=TGVSoOTuf5T5JHUVrK5wuorQF7L6Wvdem8B4uufvMJY,746
|
170
|
+
flwr/compat/simulation/__init__.py,sha256=MApGa-tysDDw34iSdxZ7TWOKtGJM-z3i8fIRJa0qbZ8,750
|
161
171
|
flwr/proto/__init__.py,sha256=S3VbQzVwNC1P-3_9EdrXuwgptO-BVuuAe20Z_OUc1cQ,683
|
162
172
|
flwr/proto/clientappio_pb2.py,sha256=aroQDv0D2GquQ5Ujqml7n7l6ObZoXqMvDa0XVO-_8Cc,3703
|
163
173
|
flwr/proto/clientappio_pb2.pyi,sha256=iL6pOPmnot5wP3aXGiDfiUpp-eJIkysyju0ebPehS8Y,5670
|
@@ -291,7 +301,7 @@ flwr/server/superlink/fleet/vce/__init__.py,sha256=XOKbAWOzlCqEOQ3M2cBYkH7HKA7Px
|
|
291
301
|
flwr/server/superlink/fleet/vce/backend/__init__.py,sha256=PPH89Yqd1XKm-sRJN6R0WQlKT_b4v54Kzl2yzHAFzM8,1437
|
292
302
|
flwr/server/superlink/fleet/vce/backend/backend.py,sha256=-wDHjgAy5mrfEgXj0GxkJI7lhEbgSUyPwmNAf9ZcDzc,2193
|
293
303
|
flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=Hx9hxL7lju1_VJoAwkhBOGerZ3628u0P1zgkPhGWRPY,7154
|
294
|
-
flwr/server/superlink/fleet/vce/vce_api.py,sha256=
|
304
|
+
flwr/server/superlink/fleet/vce/vce_api.py,sha256=zwCK4dAtZrDEAI3OxmKa9mx4ZhFqMnz6G1x2JOKqZyM,12775
|
295
305
|
flwr/server/superlink/linkstate/__init__.py,sha256=OtsgvDTnZLU3k0sUbkHbqoVwW6ql2FDmb6uT6DbNkZo,1064
|
296
306
|
flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=vvoOWjYlmOlbakH7AzpMh0jB70Qxx7UTlAGqjcA8ctM,25926
|
297
307
|
flwr/server/superlink/linkstate/linkstate.py,sha256=j6nW351t07VrBhFqjO34z8tf2PuKOE9aCX9SqpW96pQ,13100
|
@@ -315,6 +325,7 @@ flwr/server/workflow/default_workflows.py,sha256=RlD26dXbSksY-23f3ZspnN1YU1DOhDY
|
|
315
325
|
flwr/server/workflow/secure_aggregation/__init__.py,sha256=vGkycLb65CxdaMkKsANxQE6AS4urfZKvwcS3r1Vln_c,880
|
316
326
|
flwr/server/workflow/secure_aggregation/secagg_workflow.py,sha256=b_pKk7gmbahwyj0ftOOLXvu-AMtRHEc82N9PJTEO8dc,5839
|
317
327
|
flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=DkayCsnlAya6Y2PZsueLgoUCMRtV-GbnW08RfWx_SXM,29460
|
328
|
+
flwr/serverapp/__init__.py,sha256=HPvC_ZvMS7GCM7ALVrG_Wwm4bSDr4DZETeC561v3T9w,719
|
318
329
|
flwr/simulation/__init__.py,sha256=Gg6OsP1Z-ixc3-xxzvl7j7rz2Fijy9rzyEPpxgAQCeM,1556
|
319
330
|
flwr/simulation/app.py,sha256=Uy3lPwAvfZECkWPLcC0oDXTwY14e4ou8itIcBltjmWE,10437
|
320
331
|
flwr/simulation/legacy_app.py,sha256=nMISQqW0otJL1-2Kfd94O6BLlGS2IEmEPKTM2WGKrIs,15861
|
@@ -324,6 +335,7 @@ flwr/simulation/ray_transport/ray_client_proxy.py,sha256=2kVUDrJe2ViOJEuB0v_Xb3X
|
|
324
335
|
flwr/simulation/ray_transport/utils.py,sha256=KrexpWYCF-dAF3UHc9yDbPQWO-ahMT-BbD8nURLgiHk,2393
|
325
336
|
flwr/simulation/run_simulation.py,sha256=Nvw_6hI71aE2nU95_tt1F9VSo3OJWrvA97e3XZuqE4k,20310
|
326
337
|
flwr/simulation/simulationio_connection.py,sha256=mzS1C6EEREwQDPceDo30anAasmTDLb9qqV2tXlBhOUA,3494
|
338
|
+
flwr/supercore/__init__.py,sha256=pqkFoow_E6UhbBlhmoD1gmTH-33yJRhBsIZqxRPFZ7U,755
|
327
339
|
flwr/superexec/__init__.py,sha256=YFqER0IJc1XEWfsX6AxZ9LSRq0sawPYrNYki-brvTIc,715
|
328
340
|
flwr/superexec/app.py,sha256=U2jjOHb2LGWoU7vrl9_czTzre9O2mPxu3CPGUZ86sK4,1465
|
329
341
|
flwr/superexec/deployment.py,sha256=2wBBZgdNAn1Ik1M3HGg4t23CV8oZqzDz1zkOBzHjZLE,6734
|
@@ -333,7 +345,9 @@ flwr/superexec/exec_servicer.py,sha256=Z0YYfs6eNPhqn8rY0x_R04XgR2mKFpggt07IH0EhU
|
|
333
345
|
flwr/superexec/exec_user_auth_interceptor.py,sha256=iqygALkOMBUu_s_R9G0mFThZA7HTUzuXCLgxLCefiwI,4440
|
334
346
|
flwr/superexec/executor.py,sha256=M5ucqSE53jfRtuCNf59WFLqQvA1Mln4741TySeZE7qQ,3112
|
335
347
|
flwr/superexec/simulation.py,sha256=j6YwUvBN7EQ09ID7MYOCVZ70PGbuyBy8f9bXU0EszEM,4088
|
336
|
-
|
337
|
-
|
338
|
-
flwr_nightly-1.19.0.
|
339
|
-
flwr_nightly-1.19.0.
|
348
|
+
flwr/superlink/__init__.py,sha256=GNSuJ4-N6Z8wun2iZNlXqENt5beUyzC0Gi_tN396bbM,707
|
349
|
+
flwr/supernode/__init__.py,sha256=KgeCaVvXWrU3rptNR1y0oBp4YtXbAcrnCcJAiOoWkI4,707
|
350
|
+
flwr_nightly-1.19.0.dev20250520.dist-info/METADATA,sha256=HyqyQmw4kiBnhIKntjNtkH6kQb6qQ7szzRV19cj0IMA,15910
|
351
|
+
flwr_nightly-1.19.0.dev20250520.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
352
|
+
flwr_nightly-1.19.0.dev20250520.dist-info/entry_points.txt,sha256=2-1L-GNKhwGw2_7_RoH55vHw2SIHjdAQy3HAVAWl9PY,374
|
353
|
+
flwr_nightly-1.19.0.dev20250520.dist-info/RECORD,,
|
{flwr_nightly-1.19.0.dev20250516.dist-info → flwr_nightly-1.19.0.dev20250520.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|