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/superexec/deployment.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.
|
|
@@ -132,6 +132,7 @@ class DeploymentEngine(Executor):
|
|
|
132
132
|
self,
|
|
133
133
|
fab: Fab,
|
|
134
134
|
override_config: UserConfig,
|
|
135
|
+
flwr_aid: Optional[str],
|
|
135
136
|
) -> int:
|
|
136
137
|
fab_hash = self.ffs.put(fab.content, {})
|
|
137
138
|
if fab_hash != fab.hash_str:
|
|
@@ -141,7 +142,7 @@ class DeploymentEngine(Executor):
|
|
|
141
142
|
fab_id, fab_version = get_fab_metadata(fab.content)
|
|
142
143
|
|
|
143
144
|
run_id = self.linkstate.create_run(
|
|
144
|
-
fab_id, fab_version, fab_hash, override_config, ConfigRecord()
|
|
145
|
+
fab_id, fab_version, fab_hash, override_config, ConfigRecord(), flwr_aid
|
|
145
146
|
)
|
|
146
147
|
return run_id
|
|
147
148
|
|
|
@@ -161,6 +162,7 @@ class DeploymentEngine(Executor):
|
|
|
161
162
|
fab_file: bytes,
|
|
162
163
|
override_config: UserConfig,
|
|
163
164
|
federation_options: ConfigRecord,
|
|
165
|
+
flwr_aid: Optional[str],
|
|
164
166
|
) -> Optional[int]:
|
|
165
167
|
"""Start run using the Flower Deployment Engine."""
|
|
166
168
|
run_id = None
|
|
@@ -168,7 +170,9 @@ class DeploymentEngine(Executor):
|
|
|
168
170
|
|
|
169
171
|
# Call SuperLink to create run
|
|
170
172
|
run_id = self._create_run(
|
|
171
|
-
Fab(hashlib.sha256(fab_file).hexdigest(), fab_file),
|
|
173
|
+
Fab(hashlib.sha256(fab_file).hexdigest(), fab_file),
|
|
174
|
+
override_config,
|
|
175
|
+
flwr_aid,
|
|
172
176
|
)
|
|
173
177
|
|
|
174
178
|
# Register context for the Run
|
|
@@ -24,7 +24,7 @@ from google.protobuf.message import Message as GrpcMessage
|
|
|
24
24
|
from flwr.common.event_log_plugin.event_log_plugin import EventLogWriterPlugin
|
|
25
25
|
from flwr.common.typing import LogEntry
|
|
26
26
|
|
|
27
|
-
from .exec_user_auth_interceptor import
|
|
27
|
+
from .exec_user_auth_interceptor import shared_account_info
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class ExecEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
@@ -62,7 +62,7 @@ class ExecEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
62
62
|
log_entry = self.log_plugin.compose_log_before_event(
|
|
63
63
|
request=request,
|
|
64
64
|
context=context,
|
|
65
|
-
|
|
65
|
+
account_info=shared_account_info.get(),
|
|
66
66
|
method_name=method_name,
|
|
67
67
|
)
|
|
68
68
|
self.log_plugin.write_log(log_entry)
|
|
@@ -81,7 +81,7 @@ class ExecEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
81
81
|
log_entry = self.log_plugin.compose_log_after_event(
|
|
82
82
|
request=request,
|
|
83
83
|
context=context,
|
|
84
|
-
|
|
84
|
+
account_info=shared_account_info.get(),
|
|
85
85
|
method_name=method_name,
|
|
86
86
|
response=unary_response or error,
|
|
87
87
|
)
|
|
@@ -111,7 +111,7 @@ class ExecEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
111
111
|
log_entry = self.log_plugin.compose_log_after_event(
|
|
112
112
|
request=request,
|
|
113
113
|
context=context,
|
|
114
|
-
|
|
114
|
+
account_info=shared_account_info.get(),
|
|
115
115
|
method_name=method_name,
|
|
116
116
|
response=stream_response or error,
|
|
117
117
|
)
|
flwr/superexec/exec_grpc.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.
|
|
@@ -21,7 +21,7 @@ from typing import Optional
|
|
|
21
21
|
import grpc
|
|
22
22
|
|
|
23
23
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
|
24
|
-
from flwr.common.auth_plugin import ExecAuthPlugin
|
|
24
|
+
from flwr.common.auth_plugin import ExecAuthPlugin, ExecAuthzPlugin
|
|
25
25
|
from flwr.common.event_log_plugin import EventLogWriterPlugin
|
|
26
26
|
from flwr.common.grpc import generic_create_grpc_server
|
|
27
27
|
from flwr.common.logger import log
|
|
@@ -29,6 +29,7 @@ from flwr.common.typing import UserConfig
|
|
|
29
29
|
from flwr.proto.exec_pb2_grpc import add_ExecServicer_to_server
|
|
30
30
|
from flwr.server.superlink.ffs.ffs_factory import FfsFactory
|
|
31
31
|
from flwr.server.superlink.linkstate import LinkStateFactory
|
|
32
|
+
from flwr.supercore.object_store import ObjectStoreFactory
|
|
32
33
|
from flwr.superexec.exec_event_log_interceptor import ExecEventLogInterceptor
|
|
33
34
|
from flwr.superexec.exec_user_auth_interceptor import ExecUserAuthInterceptor
|
|
34
35
|
|
|
@@ -42,9 +43,11 @@ def run_exec_api_grpc(
|
|
|
42
43
|
executor: Executor,
|
|
43
44
|
state_factory: LinkStateFactory,
|
|
44
45
|
ffs_factory: FfsFactory,
|
|
46
|
+
objectstore_factory: ObjectStoreFactory,
|
|
45
47
|
certificates: Optional[tuple[bytes, bytes, bytes]],
|
|
46
48
|
config: UserConfig,
|
|
47
49
|
auth_plugin: Optional[ExecAuthPlugin] = None,
|
|
50
|
+
authz_plugin: Optional[ExecAuthzPlugin] = None,
|
|
48
51
|
event_log_plugin: Optional[EventLogWriterPlugin] = None,
|
|
49
52
|
) -> grpc.Server:
|
|
50
53
|
"""Run Exec API (gRPC, request-response)."""
|
|
@@ -53,12 +56,13 @@ def run_exec_api_grpc(
|
|
|
53
56
|
exec_servicer: grpc.Server = ExecServicer(
|
|
54
57
|
linkstate_factory=state_factory,
|
|
55
58
|
ffs_factory=ffs_factory,
|
|
59
|
+
objectstore_factory=objectstore_factory,
|
|
56
60
|
executor=executor,
|
|
57
61
|
auth_plugin=auth_plugin,
|
|
58
62
|
)
|
|
59
63
|
interceptors: list[grpc.ServerInterceptor] = []
|
|
60
|
-
if auth_plugin is not None:
|
|
61
|
-
interceptors.append(ExecUserAuthInterceptor(auth_plugin))
|
|
64
|
+
if auth_plugin is not None and authz_plugin is not None:
|
|
65
|
+
interceptors.append(ExecUserAuthInterceptor(auth_plugin, authz_plugin))
|
|
62
66
|
# Event log interceptor must be added after user auth interceptor
|
|
63
67
|
if event_log_plugin is not None:
|
|
64
68
|
interceptors.append(ExecEventLogInterceptor(event_log_plugin))
|
flwr/superexec/exec_servicer.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.
|
|
@@ -18,21 +18,25 @@
|
|
|
18
18
|
import time
|
|
19
19
|
from collections.abc import Generator
|
|
20
20
|
from logging import ERROR, INFO
|
|
21
|
-
from typing import Any, Optional
|
|
22
|
-
from uuid import UUID
|
|
21
|
+
from typing import Any, Optional, cast
|
|
23
22
|
|
|
24
23
|
import grpc
|
|
25
24
|
|
|
26
25
|
from flwr.common import now
|
|
27
26
|
from flwr.common.auth_plugin import ExecAuthPlugin
|
|
28
|
-
from flwr.common.constant import
|
|
27
|
+
from flwr.common.constant import (
|
|
28
|
+
LOG_STREAM_INTERVAL,
|
|
29
|
+
RUN_ID_NOT_FOUND_MESSAGE,
|
|
30
|
+
Status,
|
|
31
|
+
SubStatus,
|
|
32
|
+
)
|
|
29
33
|
from flwr.common.logger import log
|
|
30
34
|
from flwr.common.serde import (
|
|
31
35
|
config_record_from_proto,
|
|
32
36
|
run_to_proto,
|
|
33
37
|
user_config_from_proto,
|
|
34
38
|
)
|
|
35
|
-
from flwr.common.typing import RunStatus
|
|
39
|
+
from flwr.common.typing import Run, RunStatus
|
|
36
40
|
from flwr.proto import exec_pb2_grpc # pylint: disable=E0611
|
|
37
41
|
from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
38
42
|
GetAuthTokensRequest,
|
|
@@ -50,22 +54,26 @@ from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
|
50
54
|
)
|
|
51
55
|
from flwr.server.superlink.ffs.ffs_factory import FfsFactory
|
|
52
56
|
from flwr.server.superlink.linkstate import LinkState, LinkStateFactory
|
|
57
|
+
from flwr.supercore.object_store import ObjectStore, ObjectStoreFactory
|
|
53
58
|
|
|
59
|
+
from .exec_user_auth_interceptor import shared_account_info
|
|
54
60
|
from .executor import Executor
|
|
55
61
|
|
|
56
62
|
|
|
57
63
|
class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
58
64
|
"""SuperExec API servicer."""
|
|
59
65
|
|
|
60
|
-
def __init__(
|
|
66
|
+
def __init__( # pylint: disable=R0913, R0917
|
|
61
67
|
self,
|
|
62
68
|
linkstate_factory: LinkStateFactory,
|
|
63
69
|
ffs_factory: FfsFactory,
|
|
70
|
+
objectstore_factory: ObjectStoreFactory,
|
|
64
71
|
executor: Executor,
|
|
65
72
|
auth_plugin: Optional[ExecAuthPlugin] = None,
|
|
66
73
|
) -> None:
|
|
67
74
|
self.linkstate_factory = linkstate_factory
|
|
68
75
|
self.ffs_factory = ffs_factory
|
|
76
|
+
self.objectstore_factory = objectstore_factory
|
|
69
77
|
self.executor = executor
|
|
70
78
|
self.executor.initialize(linkstate_factory, ffs_factory)
|
|
71
79
|
self.auth_plugin = auth_plugin
|
|
@@ -76,10 +84,12 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
|
76
84
|
"""Create run ID."""
|
|
77
85
|
log(INFO, "ExecServicer.StartRun")
|
|
78
86
|
|
|
87
|
+
flwr_aid = shared_account_info.get().flwr_aid if self.auth_plugin else None
|
|
79
88
|
run_id = self.executor.start_run(
|
|
80
89
|
request.fab.content,
|
|
81
90
|
user_config_from_proto(request.override_config),
|
|
82
91
|
config_record_from_proto(request.federation_options),
|
|
92
|
+
flwr_aid,
|
|
83
93
|
)
|
|
84
94
|
|
|
85
95
|
if run_id is None:
|
|
@@ -95,12 +105,20 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
|
95
105
|
log(INFO, "ExecServicer.StreamLogs")
|
|
96
106
|
state = self.linkstate_factory.state()
|
|
97
107
|
|
|
98
|
-
# Retrieve run ID
|
|
108
|
+
# Retrieve run ID and run
|
|
99
109
|
run_id = request.run_id
|
|
110
|
+
run = state.get_run(run_id)
|
|
100
111
|
|
|
101
112
|
# Exit if `run_id` not found
|
|
102
|
-
if not
|
|
103
|
-
context.abort(grpc.StatusCode.NOT_FOUND,
|
|
113
|
+
if not run:
|
|
114
|
+
context.abort(grpc.StatusCode.NOT_FOUND, RUN_ID_NOT_FOUND_MESSAGE)
|
|
115
|
+
|
|
116
|
+
# If user auth is enabled, check if `flwr_aid` matches the run's `flwr_aid`
|
|
117
|
+
if self.auth_plugin:
|
|
118
|
+
flwr_aid = shared_account_info.get().flwr_aid
|
|
119
|
+
_check_flwr_aid_in_run(
|
|
120
|
+
flwr_aid=flwr_aid, run=cast(Run, run), context=context
|
|
121
|
+
)
|
|
104
122
|
|
|
105
123
|
after_timestamp = request.after_timestamp + 1e-6
|
|
106
124
|
while context.is_active():
|
|
@@ -119,7 +137,10 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
|
119
137
|
# is returned at this point and the server ends the stream.
|
|
120
138
|
run_status = state.get_run_status({run_id})[run_id]
|
|
121
139
|
if run_status.status == Status.FINISHED:
|
|
122
|
-
log(INFO, "All logs for run ID `%s` returned",
|
|
140
|
+
log(INFO, "All logs for run ID `%s` returned", run_id)
|
|
141
|
+
|
|
142
|
+
# Delete objects of the run from the object store
|
|
143
|
+
self.objectstore_factory.store().delete_objects_in_run(run_id)
|
|
123
144
|
break
|
|
124
145
|
|
|
125
146
|
time.sleep(LOG_STREAM_INTERVAL) # Sleep briefly to avoid busy waiting
|
|
@@ -131,11 +152,44 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
|
131
152
|
log(INFO, "ExecServicer.List")
|
|
132
153
|
state = self.linkstate_factory.state()
|
|
133
154
|
|
|
134
|
-
#
|
|
155
|
+
# Build a set of run IDs for `flwr ls --runs`
|
|
135
156
|
if not request.HasField("run_id"):
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
157
|
+
if self.auth_plugin:
|
|
158
|
+
# If no `run_id` is specified and user auth is enabled,
|
|
159
|
+
# return run IDs for the authenticated user
|
|
160
|
+
flwr_aid = shared_account_info.get().flwr_aid
|
|
161
|
+
if flwr_aid is None:
|
|
162
|
+
context.abort(
|
|
163
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
164
|
+
"️⛔️ User authentication is enabled, but `flwr_aid` is None",
|
|
165
|
+
)
|
|
166
|
+
run_ids = state.get_run_ids(flwr_aid=flwr_aid)
|
|
167
|
+
else:
|
|
168
|
+
# If no `run_id` is specified and no user auth is enabled,
|
|
169
|
+
# return all run IDs
|
|
170
|
+
run_ids = state.get_run_ids(None)
|
|
171
|
+
# Build a set of run IDs for `flwr ls --run-id <run_id>`
|
|
172
|
+
else:
|
|
173
|
+
# Retrieve run ID and run
|
|
174
|
+
run_id = request.run_id
|
|
175
|
+
run = state.get_run(run_id)
|
|
176
|
+
|
|
177
|
+
# Exit if `run_id` not found
|
|
178
|
+
if not run:
|
|
179
|
+
context.abort(grpc.StatusCode.NOT_FOUND, RUN_ID_NOT_FOUND_MESSAGE)
|
|
180
|
+
|
|
181
|
+
# If user auth is enabled, check if `flwr_aid` matches the run's `flwr_aid`
|
|
182
|
+
if self.auth_plugin:
|
|
183
|
+
flwr_aid = shared_account_info.get().flwr_aid
|
|
184
|
+
_check_flwr_aid_in_run(
|
|
185
|
+
flwr_aid=flwr_aid, run=cast(Run, run), context=context
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
run_ids = {run_id}
|
|
189
|
+
|
|
190
|
+
# Init the object store
|
|
191
|
+
store = self.objectstore_factory.store()
|
|
192
|
+
return _create_list_runs_response(run_ids, state, store)
|
|
139
193
|
|
|
140
194
|
def StopRun(
|
|
141
195
|
self, request: StopRunRequest, context: grpc.ServicerContext
|
|
@@ -144,30 +198,42 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
|
144
198
|
log(INFO, "ExecServicer.StopRun")
|
|
145
199
|
state = self.linkstate_factory.state()
|
|
146
200
|
|
|
201
|
+
# Retrieve run ID and run
|
|
202
|
+
run_id = request.run_id
|
|
203
|
+
run = state.get_run(run_id)
|
|
204
|
+
|
|
147
205
|
# Exit if `run_id` not found
|
|
148
|
-
if not
|
|
149
|
-
context.abort(
|
|
150
|
-
|
|
206
|
+
if not run:
|
|
207
|
+
context.abort(grpc.StatusCode.NOT_FOUND, RUN_ID_NOT_FOUND_MESSAGE)
|
|
208
|
+
|
|
209
|
+
# If user auth is enabled, check if `flwr_aid` matches the run's `flwr_aid`
|
|
210
|
+
if self.auth_plugin:
|
|
211
|
+
flwr_aid = shared_account_info.get().flwr_aid
|
|
212
|
+
_check_flwr_aid_in_run(
|
|
213
|
+
flwr_aid=flwr_aid, run=cast(Run, run), context=context
|
|
151
214
|
)
|
|
152
215
|
|
|
153
|
-
run_status = state.get_run_status({
|
|
216
|
+
run_status = state.get_run_status({run_id})[run_id]
|
|
154
217
|
if run_status.status == Status.FINISHED:
|
|
155
218
|
context.abort(
|
|
156
219
|
grpc.StatusCode.FAILED_PRECONDITION,
|
|
157
|
-
f"Run ID {
|
|
220
|
+
f"Run ID {run_id} is already finished",
|
|
158
221
|
)
|
|
159
222
|
|
|
160
223
|
update_success = state.update_run_status(
|
|
161
|
-
run_id=
|
|
224
|
+
run_id=run_id,
|
|
162
225
|
new_status=RunStatus(Status.FINISHED, SubStatus.STOPPED, ""),
|
|
163
226
|
)
|
|
164
227
|
|
|
165
228
|
if update_success:
|
|
166
|
-
message_ids: set[
|
|
229
|
+
message_ids: set[str] = state.get_message_ids_from_run_id(run_id)
|
|
167
230
|
|
|
168
231
|
# Delete Messages and their replies for the `run_id`
|
|
169
232
|
state.delete_messages(message_ids)
|
|
170
233
|
|
|
234
|
+
# Delete objects of the run from the object store
|
|
235
|
+
self.objectstore_factory.store().delete_objects_in_run(run_id)
|
|
236
|
+
|
|
171
237
|
return StopRunResponse(success=update_success)
|
|
172
238
|
|
|
173
239
|
def GetLoginDetails(
|
|
@@ -222,10 +288,46 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
|
222
288
|
)
|
|
223
289
|
|
|
224
290
|
|
|
225
|
-
def _create_list_runs_response(
|
|
291
|
+
def _create_list_runs_response(
|
|
292
|
+
run_ids: set[int], state: LinkState, store: ObjectStore
|
|
293
|
+
) -> ListRunsResponse:
|
|
226
294
|
"""Create response for `flwr ls --runs` and `flwr ls --run-id <run_id>`."""
|
|
227
|
-
run_dict = {run_id:
|
|
295
|
+
run_dict = {run_id: run for run_id in run_ids if (run := state.get_run(run_id))}
|
|
296
|
+
|
|
297
|
+
# Delete objects of finished runs from the object store
|
|
298
|
+
for run_id, run in run_dict.items():
|
|
299
|
+
if run.status.status == Status.FINISHED:
|
|
300
|
+
store.delete_objects_in_run(run_id)
|
|
301
|
+
|
|
228
302
|
return ListRunsResponse(
|
|
229
|
-
run_dict={run_id: run_to_proto(run) for run_id, run in run_dict.items()
|
|
303
|
+
run_dict={run_id: run_to_proto(run) for run_id, run in run_dict.items()},
|
|
230
304
|
now=now().isoformat(),
|
|
231
305
|
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _check_flwr_aid_in_run(
|
|
309
|
+
flwr_aid: Optional[str], run: Run, context: grpc.ServicerContext
|
|
310
|
+
) -> None:
|
|
311
|
+
"""Guard clause to check if `flwr_aid` matches the run's `flwr_aid`."""
|
|
312
|
+
# `flwr_aid` must not be None. Abort if it is None.
|
|
313
|
+
if flwr_aid is None:
|
|
314
|
+
context.abort(
|
|
315
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
316
|
+
"️⛔️ User authentication is enabled, but `flwr_aid` is None",
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# `run.flwr_aid` must not be an empty string. Abort if it is empty.
|
|
320
|
+
run_flwr_aid = run.flwr_aid
|
|
321
|
+
if not run_flwr_aid:
|
|
322
|
+
context.abort(
|
|
323
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
324
|
+
"⛔️ User authentication is enabled, but the run is not associated "
|
|
325
|
+
"with a `flwr_aid`.",
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Exit if `flwr_aid` does not match the run's `flwr_aid`
|
|
329
|
+
if run_flwr_aid != flwr_aid:
|
|
330
|
+
context.abort(
|
|
331
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
332
|
+
"⛔️ Run ID does not belong to the user",
|
|
333
|
+
)
|
|
@@ -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.
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
import contextvars
|
|
19
|
-
from typing import Any, Callable, Union
|
|
19
|
+
from typing import Any, Callable, Union
|
|
20
20
|
|
|
21
21
|
import grpc
|
|
22
22
|
|
|
23
|
-
from flwr.common.auth_plugin import ExecAuthPlugin
|
|
24
|
-
from flwr.common.typing import
|
|
23
|
+
from flwr.common.auth_plugin import ExecAuthPlugin, ExecAuthzPlugin
|
|
24
|
+
from flwr.common.typing import AccountInfo
|
|
25
25
|
from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
26
26
|
GetAuthTokensRequest,
|
|
27
27
|
GetAuthTokensResponse,
|
|
@@ -45,8 +45,8 @@ Response = Union[
|
|
|
45
45
|
]
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
"
|
|
48
|
+
shared_account_info: contextvars.ContextVar[AccountInfo] = contextvars.ContextVar(
|
|
49
|
+
"account_info", default=AccountInfo(flwr_aid=None, account_name=None)
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
|
|
@@ -56,8 +56,10 @@ class ExecUserAuthInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
56
56
|
def __init__(
|
|
57
57
|
self,
|
|
58
58
|
auth_plugin: ExecAuthPlugin,
|
|
59
|
+
authz_plugin: ExecAuthzPlugin,
|
|
59
60
|
):
|
|
60
61
|
self.auth_plugin = auth_plugin
|
|
62
|
+
self.authz_plugin = authz_plugin
|
|
61
63
|
|
|
62
64
|
def intercept_service(
|
|
63
65
|
self,
|
|
@@ -91,17 +93,44 @@ class ExecUserAuthInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
91
93
|
return call(request, context) # type: ignore
|
|
92
94
|
|
|
93
95
|
# For other requests, check if the user is authenticated
|
|
94
|
-
valid_tokens,
|
|
96
|
+
valid_tokens, account_info = self.auth_plugin.validate_tokens_in_metadata(
|
|
95
97
|
metadata
|
|
96
98
|
)
|
|
97
99
|
if valid_tokens:
|
|
100
|
+
if account_info is None:
|
|
101
|
+
context.abort(
|
|
102
|
+
grpc.StatusCode.UNAUTHENTICATED,
|
|
103
|
+
"Tokens validated, but user info not found",
|
|
104
|
+
)
|
|
105
|
+
raise grpc.RpcError()
|
|
98
106
|
# Store user info in contextvars for authenticated users
|
|
99
|
-
|
|
107
|
+
shared_account_info.set(account_info)
|
|
108
|
+
# Check if the user is authorized
|
|
109
|
+
if not self.authz_plugin.verify_user_authorization(account_info):
|
|
110
|
+
context.abort(
|
|
111
|
+
grpc.StatusCode.PERMISSION_DENIED, "User not authorized"
|
|
112
|
+
)
|
|
113
|
+
raise grpc.RpcError()
|
|
100
114
|
return call(request, context) # type: ignore
|
|
101
115
|
|
|
102
116
|
# If the user is not authenticated, refresh tokens
|
|
103
|
-
tokens = self.auth_plugin.refresh_tokens(
|
|
117
|
+
tokens, account_info = self.auth_plugin.refresh_tokens(metadata)
|
|
104
118
|
if tokens is not None:
|
|
119
|
+
if account_info is None:
|
|
120
|
+
context.abort(
|
|
121
|
+
grpc.StatusCode.UNAUTHENTICATED,
|
|
122
|
+
"Tokens refreshed, but user info not found",
|
|
123
|
+
)
|
|
124
|
+
raise grpc.RpcError()
|
|
125
|
+
# Store user info in contextvars for authenticated users
|
|
126
|
+
shared_account_info.set(account_info)
|
|
127
|
+
# Check if the user is authorized
|
|
128
|
+
if not self.authz_plugin.verify_user_authorization(account_info):
|
|
129
|
+
context.abort(
|
|
130
|
+
grpc.StatusCode.PERMISSION_DENIED, "User not authorized"
|
|
131
|
+
)
|
|
132
|
+
raise grpc.RpcError()
|
|
133
|
+
|
|
105
134
|
context.send_initial_metadata(tokens)
|
|
106
135
|
return call(request, context) # type: ignore
|
|
107
136
|
|
flwr/superexec/executor.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.
|
|
@@ -74,6 +74,7 @@ class Executor(ABC):
|
|
|
74
74
|
fab_file: bytes,
|
|
75
75
|
override_config: UserConfig,
|
|
76
76
|
federation_options: ConfigRecord,
|
|
77
|
+
flwr_aid: Optional[str],
|
|
77
78
|
) -> Optional[int]:
|
|
78
79
|
"""Start a run using the given Flower FAB ID and version.
|
|
79
80
|
|
|
@@ -88,6 +89,9 @@ class Executor(ABC):
|
|
|
88
89
|
The config overrides dict sent by the user (using `flwr run`).
|
|
89
90
|
federation_options: ConfigRecord
|
|
90
91
|
The federation options sent by the user (using `flwr run`).
|
|
92
|
+
flwr_aid : Optional[str]
|
|
93
|
+
The Flower Account ID of the user starting the run, if authentication is
|
|
94
|
+
enabled.
|
|
91
95
|
|
|
92
96
|
Returns
|
|
93
97
|
-------
|
flwr/superexec/simulation.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.
|
|
@@ -77,6 +77,7 @@ class SimulationEngine(Executor):
|
|
|
77
77
|
fab_file: bytes,
|
|
78
78
|
override_config: UserConfig,
|
|
79
79
|
federation_options: ConfigRecord,
|
|
80
|
+
flwr_aid: Optional[str],
|
|
80
81
|
) -> Optional[int]:
|
|
81
82
|
"""Start run using the Flower Simulation Engine."""
|
|
82
83
|
try:
|
|
@@ -96,7 +97,12 @@ class SimulationEngine(Executor):
|
|
|
96
97
|
fab_id, fab_version = get_fab_metadata(fab.content)
|
|
97
98
|
|
|
98
99
|
run_id = self.linkstate.create_run(
|
|
99
|
-
fab_id,
|
|
100
|
+
fab_id,
|
|
101
|
+
fab_version,
|
|
102
|
+
fab_hash,
|
|
103
|
+
override_config,
|
|
104
|
+
federation_options,
|
|
105
|
+
flwr_aid,
|
|
100
106
|
)
|
|
101
107
|
|
|
102
108
|
# Create an empty context for the 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
|
+
"""Flower SuperLink."""
|
|
@@ -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 SuperNode."""
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
from .app import run_supernode as run_supernode
|
|
19
|
-
|
|
20
|
-
__all__ = [
|
|
21
|
-
"run_supernode",
|
|
22
|
-
]
|
|
@@ -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.
|
|
@@ -12,20 +12,13 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
# ==============================================================================
|
|
15
|
-
"""
|
|
15
|
+
"""Flower command line interface for SuperNode."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
import
|
|
19
|
-
from
|
|
18
|
+
from .flower_supernode import flower_supernode
|
|
19
|
+
from .flwr_clientapp import flwr_clientapp
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
""
|
|
24
|
-
|
|
25
|
-
@abc.abstractmethod
|
|
26
|
-
def set_node_id(self, node_id: Optional[int]) -> None:
|
|
27
|
-
"""Set the node ID."""
|
|
28
|
-
|
|
29
|
-
@abc.abstractmethod
|
|
30
|
-
def get_node_id(self) -> int:
|
|
31
|
-
"""Get the node ID."""
|
|
21
|
+
__all__ = [
|
|
22
|
+
"flower_supernode",
|
|
23
|
+
"flwr_clientapp",
|
|
24
|
+
]
|
|
@@ -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.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
# ==============================================================================
|
|
15
|
-
"""
|
|
15
|
+
"""`flower-supernode` command."""
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
import argparse
|
|
@@ -42,12 +42,10 @@ from flwr.common.constant import (
|
|
|
42
42
|
from flwr.common.exit import ExitCode, flwr_exit
|
|
43
43
|
from flwr.common.exit_handlers import register_exit_handlers
|
|
44
44
|
from flwr.common.logger import log
|
|
45
|
+
from flwr.supernode.start_client_internal import start_client_internal
|
|
45
46
|
|
|
46
|
-
from ..app import start_client_internal
|
|
47
|
-
from ..clientapp.utils import get_load_client_app_fn
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
def run_supernode() -> None:
|
|
48
|
+
def flower_supernode() -> None:
|
|
51
49
|
"""Run Flower SuperNode."""
|
|
52
50
|
args = _parse_args_run_supernode().parse_args()
|
|
53
51
|
|
|
@@ -64,12 +62,6 @@ def run_supernode() -> None:
|
|
|
64
62
|
)
|
|
65
63
|
|
|
66
64
|
root_certificates = try_obtain_root_certificates(args, args.superlink)
|
|
67
|
-
load_fn = get_load_client_app_fn(
|
|
68
|
-
default_app_ref="",
|
|
69
|
-
app_path=None,
|
|
70
|
-
flwr_dir=args.flwr_dir,
|
|
71
|
-
multi_app=True,
|
|
72
|
-
)
|
|
73
65
|
authentication_keys = _try_setup_client_authentication(args)
|
|
74
66
|
|
|
75
67
|
log(DEBUG, "Isolation mode: %s", args.isolation)
|
|
@@ -82,7 +74,6 @@ def run_supernode() -> None:
|
|
|
82
74
|
|
|
83
75
|
start_client_internal(
|
|
84
76
|
server_address=args.superlink,
|
|
85
|
-
load_client_app_fn=load_fn,
|
|
86
77
|
transport=args.transport,
|
|
87
78
|
root_certificates=root_certificates,
|
|
88
79
|
insecure=args.insecure,
|