flwr 1.17.0__py3-none-any.whl → 1.19.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 +15 -0
- flwr/app/error.py +68 -0
- flwr/app/metadata.py +223 -0
- flwr/cli/__init__.py +1 -1
- flwr/cli/app.py +21 -2
- flwr/cli/build.py +83 -58
- flwr/cli/cli_user_auth_interceptor.py +1 -1
- flwr/cli/config_utils.py +53 -17
- flwr/cli/example.py +1 -1
- flwr/cli/install.py +1 -1
- flwr/cli/log.py +4 -4
- flwr/cli/login/__init__.py +1 -1
- flwr/cli/login/login.py +15 -8
- flwr/cli/ls.py +16 -37
- flwr/cli/new/__init__.py +1 -1
- flwr/cli/new/new.py +4 -4
- flwr/cli/new/templates/__init__.py +1 -1
- flwr/cli/new/templates/app/__init__.py +1 -1
- flwr/cli/new/templates/app/code/__init__.py +1 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +1 -1
- flwr/cli/new/templates/app/code/flwr_tune/__init__.py +1 -1
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +4 -4
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +1 -1
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +2 -3
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -17
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/run/__init__.py +1 -1
- flwr/cli/run/run.py +11 -19
- flwr/cli/stop.py +3 -3
- flwr/cli/utils.py +42 -17
- flwr/client/__init__.py +3 -3
- flwr/client/client.py +1 -1
- flwr/client/client_app.py +140 -138
- flwr/client/clientapp/__init__.py +1 -8
- flwr/client/clientapp/utils.py +1 -1
- flwr/client/dpfedavg_numpy_client.py +1 -1
- flwr/client/grpc_adapter_client/__init__.py +1 -1
- flwr/client/grpc_adapter_client/connection.py +5 -5
- flwr/client/grpc_rere_client/__init__.py +1 -1
- flwr/client/grpc_rere_client/client_interceptor.py +1 -1
- flwr/client/grpc_rere_client/connection.py +131 -61
- flwr/client/grpc_rere_client/grpc_adapter.py +35 -7
- flwr/client/message_handler/__init__.py +1 -1
- flwr/client/message_handler/message_handler.py +2 -2
- flwr/client/mod/__init__.py +1 -1
- flwr/client/mod/centraldp_mods.py +1 -1
- flwr/client/mod/comms_mods.py +39 -20
- flwr/client/mod/localdp_mod.py +6 -6
- flwr/client/mod/secure_aggregation/__init__.py +1 -1
- flwr/client/mod/secure_aggregation/secagg_mod.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
- flwr/client/mod/utils.py +1 -1
- flwr/client/numpy_client.py +1 -1
- flwr/client/rest_client/__init__.py +1 -1
- flwr/client/rest_client/connection.py +174 -68
- flwr/client/run_info_store.py +1 -1
- flwr/client/typing.py +1 -1
- flwr/clientapp/__init__.py +15 -0
- flwr/common/__init__.py +3 -3
- flwr/common/address.py +1 -1
- flwr/common/args.py +1 -1
- flwr/common/auth_plugin/__init__.py +3 -1
- flwr/common/auth_plugin/auth_plugin.py +30 -4
- flwr/common/config.py +1 -1
- flwr/common/constant.py +37 -8
- flwr/common/context.py +1 -1
- flwr/common/date.py +1 -1
- flwr/common/differential_privacy.py +1 -1
- flwr/common/differential_privacy_constants.py +1 -1
- flwr/common/dp.py +1 -1
- flwr/common/event_log_plugin/event_log_plugin.py +3 -3
- flwr/common/exit/exit.py +6 -6
- flwr/common/exit_handlers.py +31 -1
- flwr/common/grpc.py +1 -1
- flwr/common/heartbeat.py +165 -0
- flwr/common/inflatable.py +290 -0
- flwr/common/inflatable_grpc_utils.py +99 -0
- flwr/common/inflatable_rest_utils.py +99 -0
- flwr/common/inflatable_utils.py +341 -0
- flwr/common/logger.py +1 -1
- flwr/common/message.py +137 -252
- flwr/common/object_ref.py +1 -1
- flwr/common/parameter.py +1 -1
- flwr/common/pyproject.py +1 -1
- flwr/common/record/__init__.py +3 -2
- flwr/common/record/array.py +323 -0
- flwr/common/record/arrayrecord.py +121 -243
- flwr/common/record/configrecord.py +71 -16
- flwr/common/record/conversion_utils.py +2 -2
- flwr/common/record/metricrecord.py +71 -20
- flwr/common/record/recorddict.py +207 -90
- flwr/common/record/typeddict.py +1 -1
- flwr/common/recorddict_compat.py +2 -2
- flwr/common/retry_invoker.py +15 -11
- flwr/common/secure_aggregation/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/shamir.py +52 -30
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -1
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
- flwr/common/secure_aggregation/quantization.py +1 -1
- flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
- flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
- flwr/common/serde.py +60 -184
- flwr/common/serde_utils.py +175 -0
- flwr/common/telemetry.py +2 -2
- flwr/common/typing.py +6 -4
- flwr/common/version.py +1 -1
- flwr/compat/__init__.py +15 -0
- flwr/compat/client/__init__.py +15 -0
- flwr/{client → compat/client}/app.py +71 -211
- flwr/{client → compat/client}/grpc_client/__init__.py +1 -1
- flwr/{client → compat/client}/grpc_client/connection.py +13 -13
- flwr/compat/common/__init__.py +15 -0
- flwr/compat/server/__init__.py +15 -0
- flwr/compat/server/app.py +174 -0
- flwr/compat/simulation/__init__.py +15 -0
- flwr/proto/__init__.py +1 -1
- flwr/proto/fleet_pb2.py +32 -27
- flwr/proto/fleet_pb2.pyi +49 -35
- flwr/proto/fleet_pb2_grpc.py +117 -13
- flwr/proto/fleet_pb2_grpc.pyi +47 -6
- flwr/proto/heartbeat_pb2.py +33 -0
- flwr/proto/heartbeat_pb2.pyi +66 -0
- flwr/proto/heartbeat_pb2_grpc.py +4 -0
- flwr/proto/heartbeat_pb2_grpc.pyi +4 -0
- flwr/proto/message_pb2.py +28 -11
- flwr/proto/message_pb2.pyi +125 -0
- flwr/proto/recorddict_pb2.py +16 -28
- flwr/proto/recorddict_pb2.pyi +46 -64
- flwr/proto/run_pb2.py +24 -32
- flwr/proto/run_pb2.pyi +4 -52
- flwr/proto/serverappio_pb2.py +32 -23
- flwr/proto/serverappio_pb2.pyi +45 -3
- flwr/proto/serverappio_pb2_grpc.py +138 -34
- flwr/proto/serverappio_pb2_grpc.pyi +54 -13
- flwr/proto/simulationio_pb2.py +12 -11
- flwr/proto/simulationio_pb2_grpc.py +35 -0
- flwr/proto/simulationio_pb2_grpc.pyi +14 -0
- flwr/server/__init__.py +2 -2
- flwr/server/app.py +69 -187
- flwr/server/client_manager.py +1 -1
- flwr/server/client_proxy.py +1 -1
- flwr/server/compat/__init__.py +1 -1
- flwr/server/compat/app.py +1 -1
- flwr/server/compat/app_utils.py +51 -29
- flwr/server/compat/legacy_context.py +1 -1
- flwr/server/criterion.py +1 -1
- flwr/server/fleet_event_log_interceptor.py +2 -2
- flwr/server/grid/grid.py +3 -3
- flwr/server/grid/grpc_grid.py +104 -34
- flwr/server/grid/inmemory_grid.py +5 -4
- flwr/server/history.py +1 -1
- flwr/server/run_serverapp.py +1 -1
- flwr/server/server.py +1 -1
- flwr/server/server_app.py +65 -58
- flwr/server/server_config.py +1 -1
- flwr/server/serverapp/__init__.py +1 -1
- flwr/server/serverapp/app.py +19 -1
- flwr/server/serverapp_components.py +1 -1
- flwr/server/strategy/__init__.py +1 -1
- flwr/server/strategy/aggregate.py +1 -1
- flwr/server/strategy/bulyan.py +2 -2
- flwr/server/strategy/dp_adaptive_clipping.py +17 -17
- flwr/server/strategy/dp_fixed_clipping.py +17 -17
- flwr/server/strategy/dpfedavg_adaptive.py +1 -1
- flwr/server/strategy/dpfedavg_fixed.py +1 -1
- flwr/server/strategy/fault_tolerant_fedavg.py +1 -1
- flwr/server/strategy/fedadagrad.py +1 -1
- flwr/server/strategy/fedadam.py +1 -1
- flwr/server/strategy/fedavg.py +1 -1
- flwr/server/strategy/fedavg_android.py +1 -1
- flwr/server/strategy/fedavgm.py +1 -1
- flwr/server/strategy/fedmedian.py +1 -1
- flwr/server/strategy/fedopt.py +1 -1
- flwr/server/strategy/fedprox.py +1 -1
- flwr/server/strategy/fedtrimmedavg.py +1 -1
- flwr/server/strategy/fedxgb_bagging.py +1 -1
- flwr/server/strategy/fedxgb_cyclic.py +1 -1
- flwr/server/strategy/fedxgb_nn_avg.py +3 -2
- flwr/server/strategy/fedyogi.py +1 -1
- flwr/server/strategy/krum.py +1 -1
- flwr/server/strategy/qfedavg.py +1 -1
- flwr/server/strategy/strategy.py +1 -1
- flwr/server/superlink/__init__.py +1 -1
- flwr/server/superlink/ffs/__init__.py +3 -1
- flwr/server/superlink/ffs/disk_ffs.py +1 -1
- flwr/server/superlink/ffs/ffs.py +1 -1
- flwr/server/superlink/ffs/ffs_factory.py +1 -1
- flwr/server/superlink/fleet/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +14 -4
- flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +13 -13
- flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +102 -8
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +1 -1
- flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
- flwr/server/superlink/fleet/message_handler/message_handler.py +136 -19
- flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/rest_rere/rest_api.py +73 -12
- flwr/server/superlink/fleet/vce/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
- flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
- flwr/server/superlink/fleet/vce/vce_api.py +7 -4
- flwr/server/superlink/linkstate/__init__.py +1 -1
- flwr/server/superlink/linkstate/in_memory_linkstate.py +139 -44
- flwr/server/superlink/linkstate/linkstate.py +54 -21
- flwr/server/superlink/linkstate/linkstate_factory.py +1 -1
- flwr/server/superlink/linkstate/sqlite_linkstate.py +150 -56
- flwr/server/superlink/linkstate/utils.py +34 -30
- flwr/server/superlink/serverappio/serverappio_grpc.py +3 -0
- flwr/server/superlink/serverappio/serverappio_servicer.py +211 -57
- flwr/server/superlink/simulation/__init__.py +1 -1
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- flwr/server/superlink/simulation/simulationio_servicer.py +26 -2
- flwr/server/superlink/utils.py +45 -3
- flwr/server/typing.py +1 -1
- flwr/server/utils/__init__.py +1 -1
- flwr/server/utils/tensorboard.py +1 -1
- flwr/server/utils/validator.py +3 -3
- flwr/server/workflow/__init__.py +1 -1
- flwr/server/workflow/constant.py +1 -1
- flwr/server/workflow/default_workflows.py +1 -1
- flwr/server/workflow/secure_aggregation/__init__.py +1 -1
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -1
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
- flwr/serverapp/__init__.py +15 -0
- flwr/simulation/__init__.py +1 -1
- flwr/simulation/app.py +18 -1
- flwr/simulation/legacy_app.py +1 -1
- flwr/simulation/ray_transport/__init__.py +1 -1
- flwr/simulation/ray_transport/ray_actor.py +1 -1
- flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
- flwr/simulation/ray_transport/utils.py +1 -1
- flwr/simulation/run_simulation.py +2 -2
- flwr/simulation/simulationio_connection.py +1 -1
- flwr/supercore/__init__.py +15 -0
- flwr/supercore/object_store/__init__.py +24 -0
- flwr/supercore/object_store/in_memory_object_store.py +229 -0
- flwr/supercore/object_store/object_store.py +192 -0
- flwr/supercore/object_store/object_store_factory.py +44 -0
- flwr/superexec/__init__.py +1 -1
- flwr/superexec/app.py +1 -1
- flwr/superexec/deployment.py +7 -3
- flwr/superexec/exec_event_log_interceptor.py +4 -4
- flwr/superexec/exec_grpc.py +8 -4
- flwr/superexec/exec_servicer.py +126 -24
- flwr/superexec/exec_user_auth_interceptor.py +38 -9
- flwr/superexec/executor.py +5 -1
- flwr/superexec/simulation.py +8 -2
- flwr/superlink/__init__.py +15 -0
- flwr/{client/supernode → supernode}/__init__.py +1 -8
- flwr/{client/nodestate/nodestate.py → supernode/cli/__init__.py} +8 -15
- flwr/{client/supernode/app.py → supernode/cli/flower_supernode.py} +4 -13
- flwr/supernode/cli/flwr_clientapp.py +81 -0
- flwr/{client → supernode}/nodestate/__init__.py +1 -1
- flwr/supernode/nodestate/in_memory_nodestate.py +190 -0
- flwr/supernode/nodestate/nodestate.py +212 -0
- flwr/{client → supernode}/nodestate/nodestate_factory.py +1 -1
- flwr/supernode/runtime/__init__.py +15 -0
- flwr/{client/clientapp/app.py → supernode/runtime/run_clientapp.py} +26 -57
- flwr/supernode/servicer/__init__.py +15 -0
- flwr/supernode/servicer/clientappio/__init__.py +24 -0
- flwr/{client/clientapp → supernode/servicer/clientappio}/clientappio_servicer.py +1 -1
- flwr/supernode/start_client_internal.py +491 -0
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/METADATA +6 -5
- flwr-1.19.0.dist-info/RECORD +365 -0
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/WHEEL +1 -1
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/entry_points.txt +2 -2
- flwr/client/heartbeat.py +0 -74
- flwr/client/nodestate/in_memory_nodestate.py +0 -38
- flwr-1.17.0.dist-info/LICENSE +0 -202
- flwr-1.17.0.dist-info/RECORD +0 -333
flwr/cli/utils.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -28,7 +28,12 @@ import typer
|
|
|
28
28
|
|
|
29
29
|
from flwr.cli.cli_user_auth_interceptor import CliUserAuthInterceptor
|
|
30
30
|
from flwr.common.auth_plugin import CliAuthPlugin
|
|
31
|
-
from flwr.common.constant import
|
|
31
|
+
from flwr.common.constant import (
|
|
32
|
+
AUTH_TYPE_JSON_KEY,
|
|
33
|
+
CREDENTIALS_DIR,
|
|
34
|
+
FLWR_DIR,
|
|
35
|
+
RUN_ID_NOT_FOUND_MESSAGE,
|
|
36
|
+
)
|
|
32
37
|
from flwr.common.grpc import (
|
|
33
38
|
GRPC_MAX_MESSAGE_LENGTH,
|
|
34
39
|
create_channel,
|
|
@@ -220,17 +225,6 @@ def try_obtain_cli_auth_plugin(
|
|
|
220
225
|
if not federation_config.get("enable-user-auth", False):
|
|
221
226
|
return None
|
|
222
227
|
|
|
223
|
-
# Check if TLS is enabled. If not, raise an error
|
|
224
|
-
if federation_config.get("root-certificates") is None:
|
|
225
|
-
typer.secho(
|
|
226
|
-
"❌ User authentication requires TLS to be enabled. "
|
|
227
|
-
"Please provide 'root-certificates' in the federation"
|
|
228
|
-
" configuration.",
|
|
229
|
-
fg=typer.colors.RED,
|
|
230
|
-
bold=True,
|
|
231
|
-
)
|
|
232
|
-
raise typer.Exit(code=1)
|
|
233
|
-
|
|
234
228
|
config_path = get_user_auth_config_path(root_dir, federation)
|
|
235
229
|
|
|
236
230
|
# Get the auth type from the config if not provided
|
|
@@ -273,6 +267,16 @@ def init_channel(
|
|
|
273
267
|
# Initialize the CLI-side user auth interceptor
|
|
274
268
|
interceptors: list[grpc.UnaryUnaryClientInterceptor] = []
|
|
275
269
|
if auth_plugin is not None:
|
|
270
|
+
# Check if TLS is enabled. If not, raise an error
|
|
271
|
+
if insecure:
|
|
272
|
+
typer.secho(
|
|
273
|
+
"❌ User authentication requires TLS to be enabled. "
|
|
274
|
+
"Remove `insecure = true` from the federation configuration.",
|
|
275
|
+
fg=typer.colors.RED,
|
|
276
|
+
bold=True,
|
|
277
|
+
)
|
|
278
|
+
raise typer.Exit(code=1)
|
|
279
|
+
|
|
276
280
|
auth_plugin.load_tokens()
|
|
277
281
|
interceptors.append(CliUserAuthInterceptor(auth_plugin))
|
|
278
282
|
|
|
@@ -289,11 +293,12 @@ def init_channel(
|
|
|
289
293
|
|
|
290
294
|
|
|
291
295
|
@contextmanager
|
|
292
|
-
def
|
|
293
|
-
"""Context manager to handle gRPC
|
|
296
|
+
def flwr_cli_grpc_exc_handler() -> Iterator[None]:
|
|
297
|
+
"""Context manager to handle specific gRPC errors.
|
|
294
298
|
|
|
295
|
-
It catches grpc.RpcError exceptions with UNAUTHENTICATED
|
|
296
|
-
and exits the application. All other
|
|
299
|
+
It catches grpc.RpcError exceptions with UNAUTHENTICATED, UNIMPLEMENTED, and
|
|
300
|
+
PERMISSION_DENIED statuses, informs the user, and exits the application. All other
|
|
301
|
+
exceptions will be allowed to escape.
|
|
297
302
|
"""
|
|
298
303
|
try:
|
|
299
304
|
yield
|
|
@@ -313,4 +318,24 @@ def unauthenticated_exc_handler() -> Iterator[None]:
|
|
|
313
318
|
bold=True,
|
|
314
319
|
)
|
|
315
320
|
raise typer.Exit(code=1) from None
|
|
321
|
+
if e.code() == grpc.StatusCode.PERMISSION_DENIED:
|
|
322
|
+
typer.secho(
|
|
323
|
+
"❌ Authorization failed. Please contact your administrator"
|
|
324
|
+
" to check your permissions.",
|
|
325
|
+
fg=typer.colors.RED,
|
|
326
|
+
bold=True,
|
|
327
|
+
)
|
|
328
|
+
# pylint: disable=E1101
|
|
329
|
+
typer.secho(e.details(), fg=typer.colors.RED, bold=True)
|
|
330
|
+
raise typer.Exit(code=1) from None
|
|
331
|
+
if (
|
|
332
|
+
e.code() == grpc.StatusCode.NOT_FOUND
|
|
333
|
+
and e.details() == RUN_ID_NOT_FOUND_MESSAGE
|
|
334
|
+
):
|
|
335
|
+
typer.secho(
|
|
336
|
+
"❌ Run ID not found.",
|
|
337
|
+
fg=typer.colors.RED,
|
|
338
|
+
bold=True,
|
|
339
|
+
)
|
|
340
|
+
raise typer.Exit(code=1) from None
|
|
316
341
|
raise
|
flwr/client/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"""Flower client."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
from .app import start_client as start_client
|
|
19
|
-
from .app import start_numpy_client as start_numpy_client
|
|
18
|
+
from ..compat.client.app import start_client as start_client # Deprecated
|
|
19
|
+
from ..compat.client.app import start_numpy_client as start_numpy_client # Deprecated
|
|
20
20
|
from .client import Client as Client
|
|
21
21
|
from .client_app import ClientApp as ClientApp
|
|
22
22
|
from .numpy_client import NumPyClient as NumPyClient
|
flwr/client/client.py
CHANGED
flwr/client/client_app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -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
|
|
|
@@ -95,16 +95,16 @@ class ClientApp:
|
|
|
95
95
|
|
|
96
96
|
Examples
|
|
97
97
|
--------
|
|
98
|
-
Assuming a typical
|
|
99
|
-
a
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
Assuming a typical ``Client`` implementation named ``FlowerClient``, you can wrap
|
|
99
|
+
it in a ``ClientApp`` as follows::
|
|
100
|
+
|
|
101
|
+
class FlowerClient(NumPyClient):
|
|
102
|
+
# ...
|
|
103
|
+
|
|
104
|
+
def client_fn(context: Context):
|
|
105
|
+
return FlowerClient().to_client()
|
|
106
|
+
|
|
107
|
+
app = ClientApp(client_fn)
|
|
108
108
|
"""
|
|
109
109
|
|
|
110
110
|
def __init__(
|
|
@@ -181,39 +181,39 @@ class ClientApp:
|
|
|
181
181
|
|
|
182
182
|
Examples
|
|
183
183
|
--------
|
|
184
|
-
Registering a train function
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
Registering a train function with a custom action name
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
Registering a train function with a function-specific Flower Mod
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
184
|
+
Registering a train function::
|
|
185
|
+
|
|
186
|
+
app = ClientApp()
|
|
187
|
+
|
|
188
|
+
@app.train()
|
|
189
|
+
def train(message: Message, context: Context) -> Message:
|
|
190
|
+
print("Executing default train function")
|
|
191
|
+
# Create and return an echo reply message
|
|
192
|
+
return Message(message.content, reply_to=message)
|
|
193
|
+
|
|
194
|
+
Registering a train function with a custom action name::
|
|
195
|
+
|
|
196
|
+
app = ClientApp()
|
|
197
|
+
|
|
198
|
+
# Messages with `message_type="train.custom_action"` will be
|
|
199
|
+
# routed to this function.
|
|
200
|
+
@app.train("custom_action")
|
|
201
|
+
def custom_action(message: Message, context: Context) -> Message:
|
|
202
|
+
print("Executing train function for custom action")
|
|
203
|
+
return Message(message.content, reply_to=message)
|
|
204
|
+
|
|
205
|
+
Registering a train function with a function-specific Flower Mod::
|
|
206
|
+
|
|
207
|
+
from flwr.client.mod import message_size_mod
|
|
208
|
+
|
|
209
|
+
app = ClientApp()
|
|
210
|
+
|
|
211
|
+
# Using the `mods` argument to apply a function-specific mod.
|
|
212
|
+
@app.train(mods=[message_size_mod])
|
|
213
|
+
def train(message: Message, context: Context) -> Message:
|
|
214
|
+
print("Executing train function with message size mod")
|
|
215
|
+
# Create and return an echo reply message
|
|
216
|
+
return Message(message.content, reply_to=message)
|
|
217
217
|
"""
|
|
218
218
|
return _get_decorator(self, MessageType.TRAIN, action, mods)
|
|
219
219
|
|
|
@@ -236,39 +236,39 @@ class ClientApp:
|
|
|
236
236
|
|
|
237
237
|
Examples
|
|
238
238
|
--------
|
|
239
|
-
Registering an evaluate function
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
Registering an evaluate function with a custom action name
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
Registering an evaluate function with a function-specific Flower Mod
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
239
|
+
Registering an evaluate function::
|
|
240
|
+
|
|
241
|
+
app = ClientApp()
|
|
242
|
+
|
|
243
|
+
@app.evaluate()
|
|
244
|
+
def evaluate(message: Message, context: Context) -> Message:
|
|
245
|
+
print("Executing default evaluate function")
|
|
246
|
+
# Create and return an echo reply message
|
|
247
|
+
return Message(message.content, reply_to=message)
|
|
248
|
+
|
|
249
|
+
Registering an evaluate function with a custom action name::
|
|
250
|
+
|
|
251
|
+
app = ClientApp()
|
|
252
|
+
|
|
253
|
+
# Messages with `message_type="evaluate.custom_action"` will be
|
|
254
|
+
# routed to this function.
|
|
255
|
+
@app.evaluate("custom_action")
|
|
256
|
+
def custom_action(message: Message, context: Context) -> Message:
|
|
257
|
+
print("Executing evaluate function for custom action")
|
|
258
|
+
return Message(message.content, reply_to=message)
|
|
259
|
+
|
|
260
|
+
Registering an evaluate function with a function-specific Flower Mod::
|
|
261
|
+
|
|
262
|
+
from flwr.client.mod import message_size_mod
|
|
263
|
+
|
|
264
|
+
app = ClientApp()
|
|
265
|
+
|
|
266
|
+
# Using the `mods` argument to apply a function-specific mod.
|
|
267
|
+
@app.evaluate(mods=[message_size_mod])
|
|
268
|
+
def evaluate(message: Message, context: Context) -> Message:
|
|
269
|
+
print("Executing evaluate function with message size mod")
|
|
270
|
+
# Create and return an echo reply message
|
|
271
|
+
return Message(message.content, reply_to=message)
|
|
272
272
|
"""
|
|
273
273
|
return _get_decorator(self, MessageType.EVALUATE, action, mods)
|
|
274
274
|
|
|
@@ -291,39 +291,39 @@ class ClientApp:
|
|
|
291
291
|
|
|
292
292
|
Examples
|
|
293
293
|
--------
|
|
294
|
-
Registering a query function
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
Registering a query function with a custom action name
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
Registering a query function with a function-specific Flower Mod
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
294
|
+
Registering a query function::
|
|
295
|
+
|
|
296
|
+
app = ClientApp()
|
|
297
|
+
|
|
298
|
+
@app.query()
|
|
299
|
+
def query(message: Message, context: Context) -> Message:
|
|
300
|
+
print("Executing default query function")
|
|
301
|
+
# Create and return an echo reply message
|
|
302
|
+
return Message(message.content, reply_to=message)
|
|
303
|
+
|
|
304
|
+
Registering a query function with a custom action name::
|
|
305
|
+
|
|
306
|
+
app = ClientApp()
|
|
307
|
+
|
|
308
|
+
# Messages with `message_type="query.custom_action"` will be
|
|
309
|
+
# routed to this function.
|
|
310
|
+
@app.query("custom_action")
|
|
311
|
+
def custom_action(message: Message, context: Context) -> Message:
|
|
312
|
+
print("Executing query function for custom action")
|
|
313
|
+
return Message(message.content, reply_to=message)
|
|
314
|
+
|
|
315
|
+
Registering a query function with a function-specific Flower Mod::
|
|
316
|
+
|
|
317
|
+
from flwr.client.mod import message_size_mod
|
|
318
|
+
|
|
319
|
+
app = ClientApp()
|
|
320
|
+
|
|
321
|
+
# Using the `mods` argument to apply a function-specific mod.
|
|
322
|
+
@app.query(mods=[message_size_mod])
|
|
323
|
+
def query(message: Message, context: Context) -> Message:
|
|
324
|
+
print("Executing query function with message size mod")
|
|
325
|
+
# Create and return an echo reply message
|
|
326
|
+
return Message(message.content, reply_to=message)
|
|
327
327
|
"""
|
|
328
328
|
return _get_decorator(self, MessageType.QUERY, action, mods)
|
|
329
329
|
|
|
@@ -339,17 +339,19 @@ class ClientApp:
|
|
|
339
339
|
|
|
340
340
|
Examples
|
|
341
341
|
--------
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
342
|
+
::
|
|
343
|
+
|
|
344
|
+
app = ClientApp()
|
|
345
|
+
|
|
346
|
+
@app.lifespan()
|
|
347
|
+
def lifespan(context: Context) -> None:
|
|
348
|
+
# Perform initialization tasks before the app starts
|
|
349
|
+
print("Initializing ClientApp")
|
|
350
|
+
|
|
351
|
+
yield # ClientApp is running
|
|
352
|
+
|
|
353
|
+
# Perform cleanup tasks after the app stops
|
|
354
|
+
print("Cleaning up ClientApp")
|
|
353
355
|
"""
|
|
354
356
|
|
|
355
357
|
def lifespan_decorator(
|
|
@@ -436,24 +438,24 @@ def _registration_error(fn_name: str) -> ValueError:
|
|
|
436
438
|
|
|
437
439
|
Use the `ClientApp` with an existing `client_fn`:
|
|
438
440
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
441
|
+
class FlowerClient(NumPyClient):
|
|
442
|
+
# ...
|
|
443
|
+
|
|
444
|
+
def client_fn(context: Context):
|
|
445
|
+
return FlowerClient().to_client()
|
|
446
|
+
|
|
447
|
+
app = ClientApp(
|
|
448
|
+
client_fn=client_fn,
|
|
449
|
+
)
|
|
448
450
|
|
|
449
451
|
Use the `ClientApp` with a custom {fn_name} function:
|
|
450
452
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
453
|
+
app = ClientApp()
|
|
454
|
+
|
|
455
|
+
@app.{fn_name}()
|
|
456
|
+
def {fn_name}(message: Message, context: Context) -> Message:
|
|
457
|
+
print("ClientApp {fn_name} running")
|
|
458
|
+
# Create and return an echo reply message
|
|
459
|
+
return Message(message.content, reply_to=message)
|
|
458
460
|
""",
|
|
459
461
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -13,10 +13,3 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
# ==============================================================================
|
|
15
15
|
"""Flower AppIO service."""
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
from .app import flwr_clientapp as flwr_clientapp
|
|
19
|
-
|
|
20
|
-
__all__ = [
|
|
21
|
-
"flwr_clientapp",
|
|
22
|
-
]
|
flwr/client/clientapp/utils.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -45,10 +45,10 @@ def grpc_adapter( # pylint: disable=R0913,too-many-positional-arguments
|
|
|
45
45
|
tuple[
|
|
46
46
|
Callable[[], Optional[Message]],
|
|
47
47
|
Callable[[Message], None],
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
Callable[[], Optional[int]],
|
|
49
|
+
Callable[[], None],
|
|
50
|
+
Callable[[int], Run],
|
|
51
|
+
Callable[[str, int], Fab],
|
|
52
52
|
]
|
|
53
53
|
]:
|
|
54
54
|
"""Primitives for request/response-based interaction with a server via GrpcAdapter.
|