flwr 1.24.0__py3-none-any.whl → 1.26.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/__init__.py +1 -1
- flwr/app/__init__.py +4 -1
- flwr/app/message_type.py +29 -0
- flwr/app/metadata.py +5 -2
- flwr/app/user_config.py +19 -0
- flwr/cli/app.py +37 -19
- flwr/cli/app_cmd/publish.py +25 -75
- flwr/cli/app_cmd/review.py +25 -66
- flwr/cli/auth_plugin/auth_plugin.py +5 -10
- flwr/cli/auth_plugin/noop_auth_plugin.py +1 -2
- flwr/cli/auth_plugin/oidc_cli_plugin.py +38 -38
- flwr/cli/build.py +15 -28
- flwr/cli/config/__init__.py +21 -0
- flwr/cli/config/ls.py +71 -0
- flwr/cli/config_migration.py +297 -0
- flwr/cli/config_utils.py +63 -156
- flwr/cli/constant.py +71 -0
- flwr/cli/federation/__init__.py +0 -2
- flwr/cli/federation/ls.py +256 -64
- flwr/cli/flower_config.py +429 -0
- flwr/cli/install.py +23 -62
- flwr/cli/log.py +23 -37
- flwr/cli/login/login.py +29 -63
- flwr/cli/ls.py +72 -61
- flwr/cli/new/new.py +98 -309
- flwr/cli/pull.py +19 -37
- flwr/cli/run/run.py +87 -100
- flwr/cli/run_utils.py +23 -5
- flwr/cli/stop.py +33 -74
- flwr/cli/supernode/ls.py +35 -62
- flwr/cli/supernode/register.py +31 -80
- flwr/cli/supernode/unregister.py +24 -70
- flwr/cli/typing.py +200 -0
- flwr/cli/utils.py +160 -412
- flwr/client/grpc_adapter_client/connection.py +2 -2
- flwr/client/grpc_rere_client/connection.py +9 -6
- flwr/client/grpc_rere_client/grpc_adapter.py +1 -1
- flwr/client/message_handler/message_handler.py +2 -1
- flwr/client/mod/centraldp_mods.py +1 -1
- flwr/client/mod/localdp_mod.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
- flwr/client/rest_client/connection.py +6 -4
- flwr/client/run_info_store.py +2 -1
- flwr/clientapp/client_app.py +2 -1
- flwr/common/__init__.py +3 -2
- flwr/common/args.py +5 -5
- flwr/common/config.py +12 -17
- flwr/common/constant.py +3 -16
- flwr/common/context.py +2 -1
- flwr/common/exit/exit.py +4 -4
- flwr/common/exit/exit_code.py +6 -0
- flwr/common/grpc.py +2 -1
- flwr/common/logger.py +1 -1
- flwr/common/message.py +1 -1
- flwr/common/retry_invoker.py +13 -5
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +5 -2
- flwr/common/serde.py +13 -5
- flwr/common/telemetry.py +1 -1
- flwr/common/typing.py +10 -3
- flwr/compat/client/app.py +6 -9
- flwr/compat/client/grpc_client/connection.py +2 -1
- flwr/compat/common/constant.py +29 -0
- flwr/compat/server/app.py +1 -1
- flwr/proto/clientappio_pb2.py +2 -2
- flwr/proto/clientappio_pb2_grpc.py +104 -88
- flwr/proto/clientappio_pb2_grpc.pyi +140 -80
- flwr/proto/federation_pb2.py +5 -3
- flwr/proto/federation_pb2.pyi +32 -2
- flwr/proto/fleet_pb2.py +10 -10
- flwr/proto/fleet_pb2.pyi +5 -1
- flwr/proto/run_pb2.py +18 -26
- flwr/proto/run_pb2.pyi +10 -58
- flwr/proto/serverappio_pb2.py +2 -2
- flwr/proto/serverappio_pb2_grpc.py +138 -207
- flwr/proto/serverappio_pb2_grpc.pyi +189 -155
- flwr/proto/simulationio_pb2.py +2 -2
- flwr/proto/simulationio_pb2_grpc.py +62 -90
- flwr/proto/simulationio_pb2_grpc.pyi +95 -55
- flwr/server/app.py +7 -13
- flwr/server/compat/grid_client_proxy.py +2 -1
- flwr/server/grid/grpc_grid.py +5 -5
- flwr/server/serverapp/app.py +11 -4
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/node_auth_server_interceptor.py +13 -12
- flwr/server/superlink/fleet/message_handler/message_handler.py +42 -2
- flwr/server/superlink/linkstate/__init__.py +2 -2
- flwr/server/superlink/linkstate/in_memory_linkstate.py +36 -10
- flwr/server/superlink/linkstate/linkstate.py +34 -21
- flwr/server/superlink/linkstate/linkstate_factory.py +16 -8
- flwr/server/superlink/linkstate/{sqlite_linkstate.py → sql_linkstate.py} +471 -516
- flwr/server/superlink/linkstate/utils.py +49 -2
- flwr/server/superlink/serverappio/serverappio_servicer.py +1 -33
- flwr/server/superlink/simulation/simulationio_servicer.py +0 -19
- flwr/server/utils/validator.py +1 -1
- flwr/server/workflow/default_workflows.py +2 -1
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
- flwr/serverapp/strategy/bulyan.py +7 -1
- flwr/serverapp/strategy/dp_fixed_clipping.py +9 -1
- flwr/serverapp/strategy/fedavg.py +1 -1
- flwr/serverapp/strategy/fedxgb_cyclic.py +1 -1
- flwr/simulation/ray_transport/ray_client_proxy.py +2 -6
- flwr/simulation/run_simulation.py +3 -12
- flwr/simulation/simulationio_connection.py +3 -3
- flwr/{common → supercore}/address.py +7 -33
- flwr/supercore/app_utils.py +2 -1
- flwr/supercore/constant.py +27 -2
- flwr/supercore/corestate/{sqlite_corestate.py → sql_corestate.py} +19 -23
- flwr/supercore/credential_store/__init__.py +33 -0
- flwr/supercore/credential_store/credential_store.py +34 -0
- flwr/supercore/credential_store/file_credential_store.py +76 -0
- flwr/{common → supercore}/date.py +0 -11
- flwr/supercore/ffs/disk_ffs.py +1 -1
- flwr/supercore/object_store/object_store_factory.py +14 -6
- flwr/supercore/object_store/{sqlite_object_store.py → sql_object_store.py} +115 -117
- flwr/supercore/sql_mixin.py +315 -0
- flwr/{cli/new/templates → supercore/state}/__init__.py +2 -2
- flwr/{cli/new/templates/app/code/flwr_tune → supercore/state/alembic}/__init__.py +2 -2
- flwr/supercore/state/alembic/env.py +103 -0
- flwr/supercore/state/alembic/script.py.mako +43 -0
- flwr/supercore/state/alembic/utils.py +239 -0
- flwr/{cli/new/templates/app → supercore/state/alembic/versions}/__init__.py +2 -2
- flwr/supercore/state/alembic/versions/rev_2026_01_28_initialize_migration_of_state_tables.py +200 -0
- flwr/supercore/state/schema/README.md +121 -0
- flwr/{cli/new/templates/app/code → supercore/state/schema}/__init__.py +2 -2
- flwr/supercore/state/schema/corestate_tables.py +36 -0
- flwr/supercore/state/schema/linkstate_tables.py +152 -0
- flwr/supercore/state/schema/objectstore_tables.py +90 -0
- flwr/supercore/superexec/run_superexec.py +2 -2
- flwr/supercore/utils.py +225 -0
- flwr/superlink/federation/federation_manager.py +2 -2
- flwr/superlink/federation/noop_federation_manager.py +8 -6
- flwr/superlink/servicer/control/control_grpc.py +2 -0
- flwr/superlink/servicer/control/control_servicer.py +106 -21
- flwr/supernode/cli/flower_supernode.py +2 -1
- flwr/supernode/nodestate/in_memory_nodestate.py +62 -1
- flwr/supernode/nodestate/nodestate.py +45 -0
- flwr/supernode/runtime/run_clientapp.py +14 -14
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +13 -5
- flwr/supernode/start_client_internal.py +17 -10
- {flwr-1.24.0.dist-info → flwr-1.26.0.dist-info}/METADATA +8 -8
- {flwr-1.24.0.dist-info → flwr-1.26.0.dist-info}/RECORD +144 -184
- flwr/cli/federation/show.py +0 -317
- flwr/cli/new/templates/app/.gitignore.tpl +0 -163
- flwr/cli/new/templates/app/LICENSE.tpl +0 -202
- flwr/cli/new/templates/app/README.baseline.md.tpl +0 -127
- flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -68
- flwr/cli/new/templates/app/README.md.tpl +0 -37
- flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/code/__init__.py.tpl +0 -1
- flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +0 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +0 -75
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -93
- flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -71
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -102
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -46
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -80
- flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +0 -55
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -108
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -82
- flwr/cli/new/templates/app/code/client.xgboost.py.tpl +0 -110
- flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -36
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -92
- flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -87
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -56
- flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -73
- flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -78
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -66
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +0 -43
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -42
- flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -39
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -41
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -38
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -41
- flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +0 -31
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -44
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -38
- flwr/cli/new/templates/app/code/server.xgboost.py.tpl +0 -56
- flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -98
- flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -57
- flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -102
- flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -7
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -99
- flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +0 -111
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -67
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -52
- flwr/cli/new/templates/app/code/task.xgboost.py.tpl +0 -67
- flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +0 -146
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +0 -80
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +0 -65
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +0 -52
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +0 -56
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -49
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +0 -52
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +0 -61
- flwr/common/pyproject.py +0 -42
- flwr/supercore/sqlite_mixin.py +0 -159
- /flwr/{common → supercore}/version.py +0 -0
- {flwr-1.24.0.dist-info → flwr-1.26.0.dist-info}/WHEEL +0 -0
- {flwr-1.24.0.dist-info → flwr-1.26.0.dist-info}/entry_points.txt +0 -0
|
@@ -45,7 +45,7 @@ def grpc_adapter( # pylint: disable=R0913,too-many-positional-arguments
|
|
|
45
45
|
tuple[
|
|
46
46
|
int,
|
|
47
47
|
Callable[[], tuple[Message, ObjectTree] | None],
|
|
48
|
-
Callable[[Message, ObjectTree], set[str]],
|
|
48
|
+
Callable[[Message, ObjectTree, float], set[str]],
|
|
49
49
|
Callable[[int], Run],
|
|
50
50
|
Callable[[str, int], Fab],
|
|
51
51
|
Callable[[int, str], bytes],
|
|
@@ -81,7 +81,7 @@ def grpc_adapter( # pylint: disable=R0913,too-many-positional-arguments
|
|
|
81
81
|
-------
|
|
82
82
|
node_id : int
|
|
83
83
|
receive : Callable[[], Optional[tuple[Message, ObjectTree]]]
|
|
84
|
-
send : Callable[[Message, ObjectTree], set[str]]
|
|
84
|
+
send : Callable[[Message, ObjectTree, float], set[str]]
|
|
85
85
|
get_run : Callable[[int], Run]
|
|
86
86
|
get_fab : Callable[[str, int], Fab]
|
|
87
87
|
pull_object : Callable[[str], bytes]
|
|
@@ -33,7 +33,7 @@ from flwr.common.inflatable_protobuf_utils import (
|
|
|
33
33
|
)
|
|
34
34
|
from flwr.common.logger import log
|
|
35
35
|
from flwr.common.message import Message, remove_content_from_message
|
|
36
|
-
from flwr.common.retry_invoker import RetryInvoker,
|
|
36
|
+
from flwr.common.retry_invoker import RetryInvoker, wrap_stub
|
|
37
37
|
from flwr.common.serde import (
|
|
38
38
|
fab_from_proto,
|
|
39
39
|
message_from_proto,
|
|
@@ -83,7 +83,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
83
83
|
tuple[
|
|
84
84
|
int,
|
|
85
85
|
Callable[[], tuple[Message, ObjectTree] | None],
|
|
86
|
-
Callable[[Message, ObjectTree], set[str]],
|
|
86
|
+
Callable[[Message, ObjectTree, float], set[str]],
|
|
87
87
|
Callable[[int], Run],
|
|
88
88
|
Callable[[str, int], Fab],
|
|
89
89
|
Callable[[int, str], bytes],
|
|
@@ -128,7 +128,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
128
128
|
-------
|
|
129
129
|
node_id : int
|
|
130
130
|
receive : Callable[[], Optional[tuple[Message, ObjectTree]]]
|
|
131
|
-
send : Callable[[Message, ObjectTree], set[str]]
|
|
131
|
+
send : Callable[[Message, ObjectTree, float], set[str]]
|
|
132
132
|
get_run : Callable[[int], Run]
|
|
133
133
|
get_fab : Callable[[str, int], Fab]
|
|
134
134
|
pull_object : Callable[[str], bytes]
|
|
@@ -136,7 +136,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
136
136
|
confirm_message_received : Callable[[str], None]
|
|
137
137
|
"""
|
|
138
138
|
if isinstance(root_certificates, str):
|
|
139
|
-
root_certificates = Path(root_certificates).read_bytes()
|
|
139
|
+
root_certificates = Path(root_certificates).expanduser().read_bytes()
|
|
140
140
|
|
|
141
141
|
# Automatic node auth: generate keys if user didn't provide any
|
|
142
142
|
self_registered = False
|
|
@@ -165,7 +165,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
165
165
|
node: Node | None = None
|
|
166
166
|
|
|
167
167
|
# Wrap stub
|
|
168
|
-
|
|
168
|
+
wrap_stub(stub, retry_invoker)
|
|
169
169
|
###########################################################################
|
|
170
170
|
# SuperNode functions
|
|
171
171
|
###########################################################################
|
|
@@ -277,7 +277,9 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
277
277
|
# Return the Message and its object tree
|
|
278
278
|
return in_message, object_tree
|
|
279
279
|
|
|
280
|
-
def send(
|
|
280
|
+
def send(
|
|
281
|
+
message: Message, object_tree: ObjectTree, clientapp_runtime: float
|
|
282
|
+
) -> set[str]:
|
|
281
283
|
"""Send the message with its ObjectTree to SuperLink."""
|
|
282
284
|
# Get Node
|
|
283
285
|
if node is None:
|
|
@@ -293,6 +295,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
293
295
|
node=node,
|
|
294
296
|
messages_list=[message_to_proto(message)],
|
|
295
297
|
message_object_trees=[object_tree],
|
|
298
|
+
clientapp_runtime_list=[clientapp_runtime],
|
|
296
299
|
)
|
|
297
300
|
response: PushMessagesResponse = stub.PushMessages(request=request)
|
|
298
301
|
|
|
@@ -32,7 +32,6 @@ from flwr.common.constant import (
|
|
|
32
32
|
GRPC_ADAPTER_METADATA_MESSAGE_QUALNAME_KEY,
|
|
33
33
|
GRPC_ADAPTER_METADATA_SHOULD_EXIT_KEY,
|
|
34
34
|
)
|
|
35
|
-
from flwr.common.version import package_name, package_version
|
|
36
35
|
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
|
37
36
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
38
37
|
ActivateNodeRequest,
|
|
@@ -64,6 +63,7 @@ from flwr.proto.message_pb2 import ( # pylint: disable=E0611
|
|
|
64
63
|
)
|
|
65
64
|
from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
|
|
66
65
|
from flwr.supercore.constant import FORCE_EXIT_TIMEOUT_SECONDS
|
|
66
|
+
from flwr.supercore.version import package_name, package_version
|
|
67
67
|
|
|
68
68
|
T = TypeVar("T", bound=GrpcMessage)
|
|
69
69
|
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
from logging import WARN
|
|
19
19
|
from typing import cast
|
|
20
20
|
|
|
21
|
+
from flwr.app.message_type import MessageType
|
|
21
22
|
from flwr.client.client import (
|
|
22
23
|
maybe_call_evaluate,
|
|
23
24
|
maybe_call_fit,
|
|
@@ -27,7 +28,7 @@ from flwr.client.client import (
|
|
|
27
28
|
from flwr.client.numpy_client import NumPyClient
|
|
28
29
|
from flwr.client.typing import ClientFnExt
|
|
29
30
|
from flwr.common import ConfigRecord, Context, Message, Metadata, RecordDict, log
|
|
30
|
-
from flwr.common.constant import
|
|
31
|
+
from flwr.common.constant import MessageTypeLegacy
|
|
31
32
|
from flwr.common.recorddict_compat import (
|
|
32
33
|
evaluateres_to_recorddict,
|
|
33
34
|
fitres_to_recorddict,
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
from logging import INFO
|
|
19
19
|
|
|
20
|
+
from flwr.app.message_type import MessageType
|
|
20
21
|
from flwr.client.typing import ClientAppCallable
|
|
21
22
|
from flwr.common import ndarrays_to_parameters, parameters_to_ndarrays
|
|
22
23
|
from flwr.common import recorddict_compat as compat
|
|
23
|
-
from flwr.common.constant import MessageType
|
|
24
24
|
from flwr.common.context import Context
|
|
25
25
|
from flwr.common.differential_privacy import (
|
|
26
26
|
compute_adaptive_clip_model_update,
|
flwr/client/mod/localdp_mod.py
CHANGED
|
@@ -19,10 +19,10 @@ from logging import INFO
|
|
|
19
19
|
|
|
20
20
|
import numpy as np
|
|
21
21
|
|
|
22
|
+
from flwr.app.message_type import MessageType
|
|
22
23
|
from flwr.client.typing import ClientAppCallable
|
|
23
24
|
from flwr.common import ndarrays_to_parameters, parameters_to_ndarrays
|
|
24
25
|
from flwr.common import recorddict_compat as compat
|
|
25
|
-
from flwr.common.constant import MessageType
|
|
26
26
|
from flwr.common.context import Context
|
|
27
27
|
from flwr.common.differential_privacy import (
|
|
28
28
|
add_localdp_gaussian_noise_to_params,
|
|
@@ -20,6 +20,7 @@ from dataclasses import dataclass, field
|
|
|
20
20
|
from logging import DEBUG, WARNING
|
|
21
21
|
from typing import Any, cast
|
|
22
22
|
|
|
23
|
+
from flwr.app.message_type import MessageType
|
|
23
24
|
from flwr.client.typing import ClientAppCallable
|
|
24
25
|
from flwr.common import (
|
|
25
26
|
ConfigRecord,
|
|
@@ -31,7 +32,6 @@ from flwr.common import (
|
|
|
31
32
|
parameters_to_ndarrays,
|
|
32
33
|
)
|
|
33
34
|
from flwr.common import recorddict_compat as compat
|
|
34
|
-
from flwr.common.constant import MessageType
|
|
35
35
|
from flwr.common.logger import log
|
|
36
36
|
from flwr.common.secure_aggregation.crypto.shamir import create_shares
|
|
37
37
|
from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
|
|
@@ -111,7 +111,7 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
111
111
|
tuple[
|
|
112
112
|
int,
|
|
113
113
|
Callable[[], tuple[Message, ObjectTree] | None],
|
|
114
|
-
Callable[[Message, ObjectTree], set[str]],
|
|
114
|
+
Callable[[Message, ObjectTree, float], set[str]],
|
|
115
115
|
Callable[[int], Run],
|
|
116
116
|
Callable[[str, int], Fab],
|
|
117
117
|
Callable[[int, str], bytes],
|
|
@@ -149,7 +149,7 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
149
149
|
-------
|
|
150
150
|
node_id : int
|
|
151
151
|
receive : Callable[[], Optional[tuple[Message, ObjectTree]]]
|
|
152
|
-
send : Callable[[Message, ObjectTree], set[str]]
|
|
152
|
+
send : Callable[[Message, ObjectTree, float], set[str]]
|
|
153
153
|
get_run : Callable[[int], Run]
|
|
154
154
|
get_fab : Callable[[str, int], Fab]
|
|
155
155
|
pull_object : Callable[[str], bytes]
|
|
@@ -393,12 +393,13 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
393
393
|
# Return the Message and its object tree
|
|
394
394
|
return in_message, object_tree
|
|
395
395
|
|
|
396
|
-
def send(
|
|
396
|
+
def send(
|
|
397
|
+
message: Message, object_tree: ObjectTree, clientapp_runtime: float
|
|
398
|
+
) -> set[str]:
|
|
397
399
|
"""Send the message with its ObjectTree to SuperLink."""
|
|
398
400
|
# Get Node
|
|
399
401
|
if node is None:
|
|
400
402
|
raise RuntimeError("Node instance missing")
|
|
401
|
-
|
|
402
403
|
# Remove the content from the message if it has
|
|
403
404
|
if message.has_content():
|
|
404
405
|
message = remove_content_from_message(message)
|
|
@@ -408,6 +409,7 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
408
409
|
node=node,
|
|
409
410
|
messages_list=[message_to_proto(message)],
|
|
410
411
|
message_object_trees=[object_tree],
|
|
412
|
+
clientapp_runtime_list=[clientapp_runtime],
|
|
411
413
|
)
|
|
412
414
|
res = _request(req, PushMessagesResponse, PATH_PUSH_MESSAGES)
|
|
413
415
|
if res is None:
|
flwr/client/run_info_store.py
CHANGED
|
@@ -18,13 +18,14 @@
|
|
|
18
18
|
from dataclasses import dataclass
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
|
|
21
|
+
from flwr.app.user_config import UserConfig
|
|
21
22
|
from flwr.common import Context, RecordDict
|
|
22
23
|
from flwr.common.config import (
|
|
23
24
|
get_fused_config,
|
|
24
25
|
get_fused_config_from_dir,
|
|
25
26
|
get_fused_config_from_fab,
|
|
26
27
|
)
|
|
27
|
-
from flwr.common.typing import Fab, Run
|
|
28
|
+
from flwr.common.typing import Fab, Run
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
@dataclass()
|
flwr/clientapp/client_app.py
CHANGED
|
@@ -19,6 +19,7 @@ import inspect
|
|
|
19
19
|
from collections.abc import Callable, Iterator
|
|
20
20
|
from contextlib import contextmanager
|
|
21
21
|
|
|
22
|
+
from flwr.app.message_type import MessageType
|
|
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 (
|
|
@@ -26,7 +27,7 @@ from flwr.client.message_handler.message_handler import (
|
|
|
26
27
|
)
|
|
27
28
|
from flwr.client.mod.utils import make_ffn
|
|
28
29
|
from flwr.client.typing import ClientFnExt, Mod
|
|
29
|
-
from flwr.common import Context, Message
|
|
30
|
+
from flwr.common import Context, Message
|
|
30
31
|
from flwr.common.logger import warn_deprecated_feature
|
|
31
32
|
|
|
32
33
|
from .typing import ClientAppCallable
|
flwr/common/__init__.py
CHANGED
|
@@ -15,12 +15,13 @@
|
|
|
15
15
|
"""Common components shared between server and client."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
from flwr.app.message_type import MessageType as MessageType
|
|
19
|
+
|
|
18
20
|
from ..app.error import Error as Error
|
|
19
21
|
from ..app.metadata import Metadata as Metadata
|
|
20
|
-
from .
|
|
22
|
+
from ..supercore.date import now as now
|
|
21
23
|
from .constant import MessageTypeLegacy as MessageTypeLegacy
|
|
22
24
|
from .context import Context as Context
|
|
23
|
-
from .date import now as now
|
|
24
25
|
from .grpc import GRPC_MAX_MESSAGE_LENGTH
|
|
25
26
|
from .logger import configure as configure
|
|
26
27
|
from .logger import log as log
|
flwr/common/args.py
CHANGED
|
@@ -89,13 +89,13 @@ def try_obtain_root_certificates(
|
|
|
89
89
|
else:
|
|
90
90
|
# Load the certificates if provided, or load the system certificates
|
|
91
91
|
if root_cert_path is None:
|
|
92
|
-
log(INFO, "Using system certificates")
|
|
92
|
+
log(INFO, "Using system certificates for TLS connection")
|
|
93
93
|
root_certificates = None
|
|
94
94
|
elif not isfile(root_cert_path):
|
|
95
95
|
log(ERROR, "Path argument `--root-certificates` does not point to a file.")
|
|
96
96
|
sys.exit(1)
|
|
97
97
|
else:
|
|
98
|
-
root_certificates = Path(root_cert_path).read_bytes()
|
|
98
|
+
root_certificates = Path(root_cert_path).expanduser().read_bytes()
|
|
99
99
|
log(
|
|
100
100
|
DEBUG,
|
|
101
101
|
"Starting secure HTTPS channel to %s "
|
|
@@ -129,9 +129,9 @@ def try_obtain_server_certificates(
|
|
|
129
129
|
if not isfile(args.ssl_keyfile):
|
|
130
130
|
sys.exit("Path argument `--ssl-keyfile` does not point to a file.")
|
|
131
131
|
certificates = (
|
|
132
|
-
Path(args.ssl_ca_certfile).read_bytes(), # CA certificate
|
|
133
|
-
Path(args.ssl_certfile).read_bytes(), # server certificate
|
|
134
|
-
Path(args.ssl_keyfile).read_bytes(), # server private key
|
|
132
|
+
Path(args.ssl_ca_certfile).expanduser().read_bytes(), # CA certificate
|
|
133
|
+
Path(args.ssl_certfile).expanduser().read_bytes(), # server certificate
|
|
134
|
+
Path(args.ssl_keyfile).expanduser().read_bytes(), # server private key
|
|
135
135
|
)
|
|
136
136
|
return certificates
|
|
137
137
|
if args.ssl_certfile or args.ssl_keyfile or args.ssl_ca_certfile:
|
flwr/common/config.py
CHANGED
|
@@ -22,9 +22,10 @@ from io import BytesIO
|
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from typing import IO, Any, TypeVar, cast, get_args
|
|
24
24
|
|
|
25
|
+
import click
|
|
25
26
|
import tomli
|
|
26
|
-
import typer
|
|
27
27
|
|
|
28
|
+
from flwr.app.user_config import UserConfig, UserConfigValue
|
|
28
29
|
from flwr.common.constant import (
|
|
29
30
|
APP_DIR,
|
|
30
31
|
FAB_CONFIG_FILE,
|
|
@@ -32,7 +33,7 @@ from flwr.common.constant import (
|
|
|
32
33
|
FLWR_DIR,
|
|
33
34
|
FLWR_HOME,
|
|
34
35
|
)
|
|
35
|
-
from flwr.common.typing import Run
|
|
36
|
+
from flwr.common.typing import Run
|
|
36
37
|
|
|
37
38
|
from . import ConfigRecord, object_ref
|
|
38
39
|
|
|
@@ -41,7 +42,7 @@ T_dict = TypeVar("T_dict", bound=dict[str, Any]) # pylint: disable=invalid-name
|
|
|
41
42
|
|
|
42
43
|
def get_flwr_dir(provided_path: str | None = None) -> Path:
|
|
43
44
|
"""Return the Flower home directory based on env variables."""
|
|
44
|
-
if provided_path is None or not Path(provided_path).is_dir():
|
|
45
|
+
if provided_path is None or not Path(provided_path).expanduser().is_dir():
|
|
45
46
|
return Path(
|
|
46
47
|
os.getenv(
|
|
47
48
|
FLWR_HOME,
|
|
@@ -212,7 +213,7 @@ def parse_config_args(config: list[str] | None, flatten: bool = True) -> dict[st
|
|
|
212
213
|
|
|
213
214
|
# Handle if .toml file is passed
|
|
214
215
|
if len(config) == 1 and config[0].endswith(".toml"):
|
|
215
|
-
with Path(config[0]).open("rb") as config_file:
|
|
216
|
+
with Path(config[0]).expanduser().open("rb") as config_file:
|
|
216
217
|
overrides = flatten_dict(tomli.load(config_file))
|
|
217
218
|
return overrides
|
|
218
219
|
|
|
@@ -234,17 +235,13 @@ def parse_config_args(config: list[str] | None, flatten: bool = True) -> dict[st
|
|
|
234
235
|
overrides.update(tomli.loads(toml_str))
|
|
235
236
|
flat_overrides = flatten_dict(overrides) if flatten else overrides
|
|
236
237
|
except tomli.TOMLDecodeError as err:
|
|
237
|
-
|
|
238
|
-
"
|
|
238
|
+
raise click.ClickException(
|
|
239
|
+
"The provided configuration string is in an invalid format. "
|
|
239
240
|
"The correct format should be, e.g., 'key1=123 key2=false "
|
|
240
241
|
'key3="string"\', where values must be of type bool, int, '
|
|
241
242
|
"string, or float. Ensure proper formatting with "
|
|
242
|
-
"space-separated key-value pairs."
|
|
243
|
-
|
|
244
|
-
bold=True,
|
|
245
|
-
err=True,
|
|
246
|
-
)
|
|
247
|
-
raise typer.Exit(code=1) from err
|
|
243
|
+
"space-separated key-value pairs."
|
|
244
|
+
) from err
|
|
248
245
|
|
|
249
246
|
return flat_overrides
|
|
250
247
|
|
|
@@ -333,8 +330,6 @@ def validate_fields_in_config(
|
|
|
333
330
|
warnings.append('Recommended property "description" missing in [project]')
|
|
334
331
|
if "license" not in config["project"]:
|
|
335
332
|
warnings.append('Recommended property "license" missing in [project]')
|
|
336
|
-
if "authors" not in config["project"]:
|
|
337
|
-
warnings.append('Recommended property "authors" missing in [project]')
|
|
338
333
|
|
|
339
334
|
if (
|
|
340
335
|
"tool" not in config
|
|
@@ -378,13 +373,13 @@ def validate_config(
|
|
|
378
373
|
is_valid, reason = object_ref.validate(serverapp_ref, check_module, project_dir)
|
|
379
374
|
|
|
380
375
|
if not is_valid and isinstance(reason, str):
|
|
381
|
-
return False, [reason],
|
|
376
|
+
return False, [reason], warnings
|
|
382
377
|
|
|
383
378
|
# Validate clientapp
|
|
384
379
|
clientapp_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
|
|
385
380
|
is_valid, reason = object_ref.validate(clientapp_ref, check_module, project_dir)
|
|
386
381
|
|
|
387
382
|
if not is_valid and isinstance(reason, str):
|
|
388
|
-
return False, [reason],
|
|
383
|
+
return False, [reason], warnings
|
|
389
384
|
|
|
390
|
-
return True, [],
|
|
385
|
+
return True, [], warnings
|
flwr/common/constant.py
CHANGED
|
@@ -19,13 +19,11 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import os
|
|
21
21
|
|
|
22
|
-
TRANSPORT_TYPE_GRPC_BIDI = "grpc-bidi"
|
|
23
22
|
TRANSPORT_TYPE_GRPC_RERE = "grpc-rere"
|
|
24
23
|
TRANSPORT_TYPE_GRPC_ADAPTER = "grpc-adapter"
|
|
25
24
|
TRANSPORT_TYPE_REST = "rest"
|
|
26
25
|
TRANSPORT_TYPE_VCE = "vce"
|
|
27
26
|
TRANSPORT_TYPES = [
|
|
28
|
-
TRANSPORT_TYPE_GRPC_BIDI,
|
|
29
27
|
TRANSPORT_TYPE_GRPC_RERE,
|
|
30
28
|
TRANSPORT_TYPE_REST,
|
|
31
29
|
TRANSPORT_TYPE_VCE,
|
|
@@ -182,19 +180,8 @@ SUPERNODE_NOT_CREATED_FROM_CLI_MESSAGE = "Invalid SuperNode credentials"
|
|
|
182
180
|
PUBLIC_KEY_ALREADY_IN_USE_MESSAGE = "Public key already in use"
|
|
183
181
|
PUBLIC_KEY_NOT_VALID = "The provided public key is not valid"
|
|
184
182
|
NODE_NOT_FOUND_MESSAGE = "Node ID not found for account"
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
class MessageType:
|
|
188
|
-
"""Message type."""
|
|
189
|
-
|
|
190
|
-
TRAIN = "train"
|
|
191
|
-
EVALUATE = "evaluate"
|
|
192
|
-
QUERY = "query"
|
|
193
|
-
SYSTEM = "system"
|
|
194
|
-
|
|
195
|
-
def __new__(cls) -> MessageType:
|
|
196
|
-
"""Prevent instantiation."""
|
|
197
|
-
raise TypeError(f"{cls.__name__} cannot be instantiated.")
|
|
183
|
+
FEDERATION_NOT_SPECIFIED_MESSAGE = "No federation specified in the request"
|
|
184
|
+
FEDERATION_NOT_FOUND_MESSAGE = "Federation '%s' does not exist"
|
|
198
185
|
|
|
199
186
|
|
|
200
187
|
class MessageTypeLegacy:
|
|
@@ -323,4 +310,4 @@ class ExecPluginType:
|
|
|
323
310
|
|
|
324
311
|
# Constants for No-op auth plugins
|
|
325
312
|
NOOP_FLWR_AID = "<id:none>"
|
|
326
|
-
NOOP_ACCOUNT_NAME = "
|
|
313
|
+
NOOP_ACCOUNT_NAME = "none"
|
flwr/common/context.py
CHANGED
flwr/common/exit/exit.py
CHANGED
|
@@ -23,8 +23,8 @@ from logging import ERROR, INFO
|
|
|
23
23
|
from typing import Any, NoReturn
|
|
24
24
|
|
|
25
25
|
from flwr.common import EventType, event
|
|
26
|
-
from flwr.common.version import package_version
|
|
27
26
|
from flwr.supercore.constant import FORCE_EXIT_TIMEOUT_SECONDS
|
|
27
|
+
from flwr.supercore.version import package_version
|
|
28
28
|
|
|
29
29
|
from ..logger import log
|
|
30
30
|
from .exit_code import EXIT_CODE_HELP
|
|
@@ -87,9 +87,6 @@ def flwr_exit(
|
|
|
87
87
|
# Log the exit message
|
|
88
88
|
log(log_level, exit_message)
|
|
89
89
|
|
|
90
|
-
# Trigger exit handlers
|
|
91
|
-
trigger_exit_handlers()
|
|
92
|
-
|
|
93
90
|
# Start a daemon thread to force exit if graceful exit fails
|
|
94
91
|
def force_exit() -> None:
|
|
95
92
|
time.sleep(FORCE_EXIT_TIMEOUT_SECONDS)
|
|
@@ -97,6 +94,9 @@ def flwr_exit(
|
|
|
97
94
|
|
|
98
95
|
threading.Thread(target=force_exit, daemon=True).start()
|
|
99
96
|
|
|
97
|
+
# Trigger exit handlers
|
|
98
|
+
trigger_exit_handlers()
|
|
99
|
+
|
|
100
100
|
# Exit
|
|
101
101
|
sys.exit(sys_exit_code)
|
|
102
102
|
|
flwr/common/exit/exit_code.py
CHANGED
|
@@ -33,6 +33,7 @@ class ExitCode:
|
|
|
33
33
|
SUPERLINK_LICENSE_MISSING = 102
|
|
34
34
|
SUPERLINK_LICENSE_URL_INVALID = 103
|
|
35
35
|
SUPERLINK_INVALID_ARGS = 104
|
|
36
|
+
SUPERLINK_DATABASE_SCHEMA_MISMATCH = 105
|
|
36
37
|
|
|
37
38
|
# ServerApp-specific exit codes (200-299)
|
|
38
39
|
SERVERAPP_STRATEGY_PRECONDITION_UNMET = 200
|
|
@@ -91,6 +92,11 @@ EXIT_CODE_HELP = {
|
|
|
91
92
|
"Invalid arguments provided to SuperLink. Use `--help` check for the correct "
|
|
92
93
|
"usage. Alternatively, check the documentation."
|
|
93
94
|
),
|
|
95
|
+
ExitCode.SUPERLINK_DATABASE_SCHEMA_MISMATCH: (
|
|
96
|
+
"The database schema does not match the expected schema for this version of "
|
|
97
|
+
"SuperLink. Please refer to the documentation for guidance on how to resolve "
|
|
98
|
+
"this issue."
|
|
99
|
+
),
|
|
94
100
|
# ServerApp-specific exit codes (200-299)
|
|
95
101
|
ExitCode.SERVERAPP_STRATEGY_PRECONDITION_UNMET: (
|
|
96
102
|
"The strategy received replies that cannot be aggregated. Please ensure all "
|
flwr/common/grpc.py
CHANGED
flwr/common/logger.py
CHANGED
|
@@ -385,7 +385,7 @@ def start_log_uploader(
|
|
|
385
385
|
) -> threading.Thread:
|
|
386
386
|
"""Start the log uploader thread and return it."""
|
|
387
387
|
thread = threading.Thread(
|
|
388
|
-
target=_log_uploader, args=(log_queue, node_id, run_id, stub)
|
|
388
|
+
target=_log_uploader, args=(log_queue, node_id, run_id, stub), daemon=True
|
|
389
389
|
)
|
|
390
390
|
thread.start()
|
|
391
391
|
return thread
|
flwr/common/message.py
CHANGED
|
@@ -20,11 +20,11 @@ from __future__ import annotations
|
|
|
20
20
|
from logging import WARNING
|
|
21
21
|
from typing import Any, cast, overload
|
|
22
22
|
|
|
23
|
-
from flwr.common.date import now
|
|
24
23
|
from flwr.common.logger import warn_deprecated_feature
|
|
25
24
|
from flwr.proto.message_pb2 import Message as ProtoMessage # pylint: disable=E0611
|
|
26
25
|
from flwr.proto.message_pb2 import Metadata as ProtoMetadata # pylint: disable=E0611
|
|
27
26
|
from flwr.proto.message_pb2 import ObjectIDs # pylint: disable=E0611
|
|
27
|
+
from flwr.supercore.date import now
|
|
28
28
|
|
|
29
29
|
from ..app.error import Error
|
|
30
30
|
from ..app.metadata import Metadata
|
flwr/common/retry_invoker.py
CHANGED
|
@@ -21,7 +21,7 @@ import threading
|
|
|
21
21
|
import time
|
|
22
22
|
from collections.abc import Callable, Generator, Iterable
|
|
23
23
|
from dataclasses import dataclass
|
|
24
|
-
from logging import INFO, WARN
|
|
24
|
+
from logging import DEBUG, ERROR, INFO, WARN
|
|
25
25
|
from typing import Any, cast
|
|
26
26
|
|
|
27
27
|
import grpc
|
|
@@ -318,7 +318,7 @@ class RetryInvoker:
|
|
|
318
318
|
return ret
|
|
319
319
|
|
|
320
320
|
|
|
321
|
-
def
|
|
321
|
+
def make_simple_grpc_retry_invoker() -> RetryInvoker:
|
|
322
322
|
"""Create a simple gRPC retry invoker."""
|
|
323
323
|
lock = threading.Lock()
|
|
324
324
|
system_healthy = threading.Event()
|
|
@@ -334,8 +334,11 @@ def _make_simple_grpc_retry_invoker() -> RetryInvoker:
|
|
|
334
334
|
retry_state.tries,
|
|
335
335
|
)
|
|
336
336
|
|
|
337
|
-
def _on_backoff(
|
|
337
|
+
def _on_backoff(retry_state: RetryState) -> None:
|
|
338
338
|
system_healthy.clear()
|
|
339
|
+
log(
|
|
340
|
+
DEBUG, "Connection attempt failed with exception: %s", retry_state.exception
|
|
341
|
+
)
|
|
339
342
|
|
|
340
343
|
def _on_giveup(retry_state: RetryState) -> None:
|
|
341
344
|
system_healthy.clear()
|
|
@@ -351,7 +354,12 @@ def _make_simple_grpc_retry_invoker() -> RetryInvoker:
|
|
|
351
354
|
if e.code() == grpc.StatusCode.PERMISSION_DENIED: # type: ignore
|
|
352
355
|
raise RunNotRunningException
|
|
353
356
|
if e.code() == grpc.StatusCode.UNAVAILABLE: # type: ignore
|
|
354
|
-
|
|
357
|
+
# Check if this is an SSL handshake failure - these should fail fast
|
|
358
|
+
details = str(e.details() if hasattr(e, "details") else "").lower()
|
|
359
|
+
if "handshake failed" in details:
|
|
360
|
+
log(ERROR, "SSL/TLS handshake error detected.")
|
|
361
|
+
return True # Give up on SSL/TLS handshake errors
|
|
362
|
+
return False # Retry on other UNAVAILABLE errors (network issues)
|
|
355
363
|
return True
|
|
356
364
|
|
|
357
365
|
def _wait(wait_time: float) -> None:
|
|
@@ -386,7 +394,7 @@ def _make_simple_grpc_retry_invoker() -> RetryInvoker:
|
|
|
386
394
|
)
|
|
387
395
|
|
|
388
396
|
|
|
389
|
-
def
|
|
397
|
+
def wrap_stub(
|
|
390
398
|
stub: (
|
|
391
399
|
ServerAppIoStub | ClientAppIoStub | SimulationIoStub | FleetStub | GrpcAdapter
|
|
392
400
|
),
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
from typing import Any
|
|
19
19
|
|
|
20
20
|
import numpy as np
|
|
21
|
-
from numpy.typing import
|
|
21
|
+
from numpy.typing import NDArray
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def factor_combine(factor: int, parameters: list[NDArray[Any]]) -> list[NDArray[Any]]:
|
|
@@ -38,8 +38,11 @@ def get_parameters_shape(parameters: list[NDArray[Any]]) -> list[tuple[int, ...]
|
|
|
38
38
|
return [arr.shape for arr in parameters]
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
default_numpy_dtype = np.dtype(np.int64)
|
|
42
|
+
|
|
43
|
+
|
|
41
44
|
def get_zero_parameters(
|
|
42
|
-
dimensions_list: list[tuple[int, ...]], dtype:
|
|
45
|
+
dimensions_list: list[tuple[int, ...]], dtype: np.dtype[Any] = default_numpy_dtype
|
|
43
46
|
) -> list[NDArray[Any]]:
|
|
44
47
|
"""Generate zero parameters based on the dimensions list."""
|
|
45
48
|
return [np.zeros(dimensions, dtype=dtype) for dimensions in dimensions_list]
|
flwr/common/serde.py
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
from typing import Any, cast
|
|
19
19
|
|
|
20
|
+
from flwr.app.user_config import UserConfig, UserConfigValue
|
|
21
|
+
|
|
20
22
|
# pylint: disable=E0611
|
|
21
23
|
from flwr.proto.fab_pb2 import Fab as ProtoFab
|
|
22
24
|
from flwr.proto.message_pb2 import Context as ProtoContext
|
|
@@ -513,7 +515,7 @@ def fab_from_proto(fab: ProtoFab) -> typing.Fab:
|
|
|
513
515
|
# === User configs ===
|
|
514
516
|
|
|
515
517
|
|
|
516
|
-
def user_config_to_proto(user_config:
|
|
518
|
+
def user_config_to_proto(user_config: UserConfig) -> Any:
|
|
517
519
|
"""Serialize `UserConfig` to ProtoBuf."""
|
|
518
520
|
proto = {}
|
|
519
521
|
for key, value in user_config.items():
|
|
@@ -521,7 +523,7 @@ def user_config_to_proto(user_config: typing.UserConfig) -> Any:
|
|
|
521
523
|
return proto
|
|
522
524
|
|
|
523
525
|
|
|
524
|
-
def user_config_from_proto(proto: Any) ->
|
|
526
|
+
def user_config_from_proto(proto: Any) -> UserConfig:
|
|
525
527
|
"""Deserialize `UserConfig` from ProtoBuf."""
|
|
526
528
|
metrics = {}
|
|
527
529
|
for key, value in proto.items():
|
|
@@ -529,7 +531,7 @@ def user_config_from_proto(proto: Any) -> typing.UserConfig:
|
|
|
529
531
|
return metrics
|
|
530
532
|
|
|
531
533
|
|
|
532
|
-
def user_config_value_to_proto(user_config_value:
|
|
534
|
+
def user_config_value_to_proto(user_config_value: UserConfigValue) -> Scalar:
|
|
533
535
|
"""Serialize `UserConfigValue` to ProtoBuf."""
|
|
534
536
|
if isinstance(user_config_value, bool):
|
|
535
537
|
return Scalar(bool=user_config_value)
|
|
@@ -548,11 +550,11 @@ def user_config_value_to_proto(user_config_value: typing.UserConfigValue) -> Sca
|
|
|
548
550
|
)
|
|
549
551
|
|
|
550
552
|
|
|
551
|
-
def user_config_value_from_proto(scalar_msg: Scalar) ->
|
|
553
|
+
def user_config_value_from_proto(scalar_msg: Scalar) -> UserConfigValue:
|
|
552
554
|
"""Deserialize `UserConfigValue` from ProtoBuf."""
|
|
553
555
|
scalar_field = scalar_msg.WhichOneof("scalar")
|
|
554
556
|
scalar = getattr(scalar_msg, cast(str, scalar_field))
|
|
555
|
-
return cast(
|
|
557
|
+
return cast(UserConfigValue, scalar)
|
|
556
558
|
|
|
557
559
|
|
|
558
560
|
# === Message messages ===
|
|
@@ -632,6 +634,9 @@ def run_to_proto(run: typing.Run) -> ProtoRun:
|
|
|
632
634
|
status=run_status_to_proto(run.status),
|
|
633
635
|
flwr_aid=run.flwr_aid,
|
|
634
636
|
federation=run.federation,
|
|
637
|
+
bytes_sent=run.bytes_sent,
|
|
638
|
+
bytes_recv=run.bytes_recv,
|
|
639
|
+
clientapp_runtime=run.clientapp_runtime,
|
|
635
640
|
)
|
|
636
641
|
return proto
|
|
637
642
|
|
|
@@ -651,6 +656,9 @@ def run_from_proto(run_proto: ProtoRun) -> typing.Run:
|
|
|
651
656
|
status=run_status_from_proto(run_proto.status),
|
|
652
657
|
flwr_aid=run_proto.flwr_aid,
|
|
653
658
|
federation=run_proto.federation,
|
|
659
|
+
bytes_sent=run_proto.bytes_sent,
|
|
660
|
+
bytes_recv=run_proto.bytes_recv,
|
|
661
|
+
clientapp_runtime=run_proto.clientapp_runtime,
|
|
654
662
|
)
|
|
655
663
|
return run
|
|
656
664
|
|
flwr/common/telemetry.py
CHANGED
|
@@ -28,7 +28,7 @@ from pathlib import Path
|
|
|
28
28
|
from typing import Any, cast
|
|
29
29
|
|
|
30
30
|
from flwr.common.constant import FLWR_DIR
|
|
31
|
-
from flwr.
|
|
31
|
+
from flwr.supercore.version import package_name, package_version
|
|
32
32
|
|
|
33
33
|
FLWR_TELEMETRY_ENABLED = os.getenv("FLWR_TELEMETRY_ENABLED", "1")
|
|
34
34
|
FLWR_TELEMETRY_LOGGING = os.getenv("FLWR_TELEMETRY_LOGGING", "0")
|