flwr 1.20.0__py3-none-any.whl → 1.22.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 +4 -1
- flwr/app/__init__.py +28 -0
- flwr/app/exception.py +31 -0
- flwr/cli/app.py +2 -0
- flwr/cli/auth_plugin/oidc_cli_plugin.py +4 -4
- flwr/cli/cli_user_auth_interceptor.py +1 -1
- flwr/cli/config_utils.py +3 -3
- flwr/cli/constant.py +25 -8
- flwr/cli/log.py +9 -9
- flwr/cli/login/login.py +3 -3
- flwr/cli/ls.py +5 -5
- flwr/cli/new/new.py +15 -2
- flwr/cli/new/templates/app/README.flowertune.md.tpl +1 -1
- flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +1 -0
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +64 -47
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl +68 -30
- flwr/cli/new/templates/app/code/client.jax.py.tpl +63 -42
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +80 -51
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +36 -13
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +71 -46
- flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +55 -0
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +75 -30
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +69 -44
- flwr/cli/new/templates/app/code/client.xgboost.py.tpl +110 -0
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +56 -90
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +1 -23
- flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +37 -58
- flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +39 -44
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -14
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +27 -29
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +23 -19
- flwr/cli/new/templates/app/code/server.jax.py.tpl +27 -14
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +29 -19
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +30 -17
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +36 -26
- flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +31 -0
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +29 -21
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +28 -19
- flwr/cli/new/templates/app/code/server.xgboost.py.tpl +56 -0
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl +16 -20
- flwr/cli/new/templates/app/code/task.jax.py.tpl +1 -1
- flwr/cli/new/templates/app/code/task.numpy.py.tpl +1 -1
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +14 -27
- flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +111 -0
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +1 -2
- flwr/cli/new/templates/app/code/task.xgboost.py.tpl +67 -0
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +3 -3
- flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +53 -0
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +61 -0
- flwr/cli/pull.py +100 -0
- flwr/cli/run/run.py +9 -13
- flwr/cli/stop.py +7 -4
- flwr/cli/utils.py +36 -8
- flwr/client/grpc_rere_client/connection.py +1 -12
- flwr/client/rest_client/connection.py +3 -0
- flwr/clientapp/__init__.py +10 -0
- flwr/clientapp/mod/__init__.py +29 -0
- flwr/clientapp/mod/centraldp_mods.py +248 -0
- flwr/clientapp/mod/localdp_mod.py +169 -0
- flwr/clientapp/typing.py +22 -0
- flwr/common/args.py +20 -6
- flwr/common/auth_plugin/__init__.py +4 -4
- flwr/common/auth_plugin/auth_plugin.py +7 -7
- flwr/common/constant.py +26 -4
- flwr/common/event_log_plugin/event_log_plugin.py +1 -1
- flwr/common/exit/__init__.py +4 -0
- flwr/common/exit/exit.py +8 -1
- flwr/common/exit/exit_code.py +30 -7
- flwr/common/exit/exit_handler.py +62 -0
- flwr/common/{exit_handlers.py → exit/signal_handler.py} +20 -37
- flwr/common/grpc.py +0 -11
- flwr/common/inflatable_utils.py +1 -1
- flwr/common/logger.py +1 -1
- flwr/common/record/typeddict.py +12 -0
- flwr/common/retry_invoker.py +30 -11
- flwr/common/telemetry.py +4 -0
- flwr/compat/server/app.py +2 -2
- flwr/proto/appio_pb2.py +25 -17
- flwr/proto/appio_pb2.pyi +46 -2
- flwr/proto/clientappio_pb2.py +3 -11
- flwr/proto/clientappio_pb2.pyi +0 -47
- flwr/proto/clientappio_pb2_grpc.py +19 -20
- flwr/proto/clientappio_pb2_grpc.pyi +10 -11
- flwr/proto/control_pb2.py +66 -0
- flwr/proto/{exec_pb2.pyi → control_pb2.pyi} +24 -0
- flwr/proto/{exec_pb2_grpc.py → control_pb2_grpc.py} +88 -54
- flwr/proto/control_pb2_grpc.pyi +106 -0
- flwr/proto/serverappio_pb2.py +2 -2
- flwr/proto/serverappio_pb2_grpc.py +68 -0
- flwr/proto/serverappio_pb2_grpc.pyi +26 -0
- flwr/proto/simulationio_pb2.py +4 -11
- flwr/proto/simulationio_pb2.pyi +0 -58
- flwr/proto/simulationio_pb2_grpc.py +129 -27
- flwr/proto/simulationio_pb2_grpc.pyi +52 -13
- flwr/server/app.py +142 -152
- flwr/server/grid/grpc_grid.py +3 -0
- flwr/server/grid/inmemory_grid.py +1 -0
- flwr/server/serverapp/app.py +157 -146
- flwr/server/superlink/fleet/vce/backend/raybackend.py +3 -1
- flwr/server/superlink/fleet/vce/vce_api.py +6 -6
- flwr/server/superlink/linkstate/in_memory_linkstate.py +34 -0
- flwr/server/superlink/linkstate/linkstate.py +2 -1
- flwr/server/superlink/linkstate/sqlite_linkstate.py +45 -0
- flwr/server/superlink/serverappio/serverappio_grpc.py +1 -1
- flwr/server/superlink/serverappio/serverappio_servicer.py +61 -6
- flwr/server/superlink/simulation/simulationio_servicer.py +97 -21
- flwr/serverapp/__init__.py +12 -0
- flwr/serverapp/exception.py +38 -0
- flwr/serverapp/strategy/__init__.py +64 -0
- flwr/serverapp/strategy/bulyan.py +238 -0
- flwr/serverapp/strategy/dp_adaptive_clipping.py +335 -0
- flwr/serverapp/strategy/dp_fixed_clipping.py +374 -0
- flwr/serverapp/strategy/fedadagrad.py +159 -0
- flwr/serverapp/strategy/fedadam.py +178 -0
- flwr/serverapp/strategy/fedavg.py +320 -0
- flwr/serverapp/strategy/fedavgm.py +198 -0
- flwr/serverapp/strategy/fedmedian.py +105 -0
- flwr/serverapp/strategy/fedopt.py +218 -0
- flwr/serverapp/strategy/fedprox.py +174 -0
- flwr/serverapp/strategy/fedtrimmedavg.py +176 -0
- flwr/serverapp/strategy/fedxgb_bagging.py +117 -0
- flwr/serverapp/strategy/fedxgb_cyclic.py +220 -0
- flwr/serverapp/strategy/fedyogi.py +170 -0
- flwr/serverapp/strategy/krum.py +112 -0
- flwr/serverapp/strategy/multikrum.py +247 -0
- flwr/serverapp/strategy/qfedavg.py +252 -0
- flwr/serverapp/strategy/result.py +105 -0
- flwr/serverapp/strategy/strategy.py +285 -0
- flwr/serverapp/strategy/strategy_utils.py +299 -0
- flwr/simulation/app.py +161 -164
- flwr/simulation/run_simulation.py +25 -30
- flwr/supercore/app_utils.py +58 -0
- flwr/{supernode/scheduler → supercore/cli}/__init__.py +3 -3
- flwr/supercore/cli/flower_superexec.py +166 -0
- flwr/supercore/constant.py +19 -0
- flwr/supercore/{scheduler → corestate}/__init__.py +3 -3
- flwr/supercore/corestate/corestate.py +81 -0
- flwr/supercore/grpc_health/__init__.py +3 -0
- flwr/supercore/grpc_health/health_server.py +53 -0
- flwr/supercore/grpc_health/simple_health_servicer.py +2 -2
- flwr/{superexec → supercore/superexec}/__init__.py +1 -1
- flwr/supercore/superexec/plugin/__init__.py +28 -0
- flwr/{supernode/scheduler/simple_clientapp_scheduler_plugin.py → supercore/superexec/plugin/base_exec_plugin.py} +10 -6
- flwr/supercore/superexec/plugin/clientapp_exec_plugin.py +28 -0
- flwr/supercore/{scheduler/plugin.py → superexec/plugin/exec_plugin.py} +15 -5
- flwr/supercore/superexec/plugin/serverapp_exec_plugin.py +28 -0
- flwr/supercore/superexec/plugin/simulation_exec_plugin.py +28 -0
- flwr/supercore/superexec/run_superexec.py +199 -0
- flwr/superlink/artifact_provider/__init__.py +22 -0
- flwr/superlink/artifact_provider/artifact_provider.py +37 -0
- flwr/superlink/servicer/__init__.py +15 -0
- flwr/superlink/servicer/control/__init__.py +22 -0
- flwr/{superexec/exec_event_log_interceptor.py → superlink/servicer/control/control_event_log_interceptor.py} +7 -7
- flwr/{superexec/exec_grpc.py → superlink/servicer/control/control_grpc.py} +27 -29
- flwr/{superexec/exec_license_interceptor.py → superlink/servicer/control/control_license_interceptor.py} +6 -6
- flwr/{superexec/exec_servicer.py → superlink/servicer/control/control_servicer.py} +127 -31
- flwr/{superexec/exec_user_auth_interceptor.py → superlink/servicer/control/control_user_auth_interceptor.py} +10 -10
- flwr/supernode/cli/flower_supernode.py +3 -0
- flwr/supernode/cli/flwr_clientapp.py +18 -21
- flwr/supernode/nodestate/in_memory_nodestate.py +2 -2
- flwr/supernode/nodestate/nodestate.py +3 -59
- flwr/supernode/runtime/run_clientapp.py +39 -102
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +10 -17
- flwr/supernode/start_client_internal.py +35 -76
- {flwr-1.20.0.dist-info → flwr-1.22.0.dist-info}/METADATA +9 -18
- {flwr-1.20.0.dist-info → flwr-1.22.0.dist-info}/RECORD +176 -128
- {flwr-1.20.0.dist-info → flwr-1.22.0.dist-info}/entry_points.txt +1 -0
- flwr/proto/exec_pb2.py +0 -62
- flwr/proto/exec_pb2_grpc.pyi +0 -93
- flwr/superexec/app.py +0 -45
- flwr/superexec/deployment.py +0 -191
- flwr/superexec/executor.py +0 -100
- flwr/superexec/simulation.py +0 -129
- {flwr-1.20.0.dist-info → flwr-1.22.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
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-superexec` command."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
from logging import INFO
|
|
20
|
+
from typing import Any, Optional
|
|
21
|
+
|
|
22
|
+
import yaml
|
|
23
|
+
|
|
24
|
+
from flwr.common import EventType, event
|
|
25
|
+
from flwr.common.constant import ExecPluginType
|
|
26
|
+
from flwr.common.exit import ExitCode, flwr_exit
|
|
27
|
+
from flwr.common.logger import log
|
|
28
|
+
from flwr.proto.clientappio_pb2_grpc import ClientAppIoStub
|
|
29
|
+
from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub
|
|
30
|
+
from flwr.proto.simulationio_pb2_grpc import SimulationIoStub
|
|
31
|
+
from flwr.supercore.constant import EXEC_PLUGIN_SECTION
|
|
32
|
+
from flwr.supercore.grpc_health import add_args_health
|
|
33
|
+
from flwr.supercore.superexec.plugin import (
|
|
34
|
+
ClientAppExecPlugin,
|
|
35
|
+
ExecPlugin,
|
|
36
|
+
ServerAppExecPlugin,
|
|
37
|
+
SimulationExecPlugin,
|
|
38
|
+
)
|
|
39
|
+
from flwr.supercore.superexec.run_superexec import run_superexec
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
from flwr.ee import add_ee_args_superexec
|
|
43
|
+
from flwr.ee.constant import ExecEePluginType
|
|
44
|
+
from flwr.ee.exec_plugin import get_ee_plugin_and_stub_class
|
|
45
|
+
except ImportError:
|
|
46
|
+
|
|
47
|
+
class ExecEePluginType: # type: ignore[no-redef]
|
|
48
|
+
"""SuperExec EE plugin types."""
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def all() -> list[str]:
|
|
52
|
+
"""Return all SuperExec EE plugin types."""
|
|
53
|
+
return []
|
|
54
|
+
|
|
55
|
+
def get_ee_plugin_and_stub_class( # pylint: disable=unused-argument
|
|
56
|
+
plugin_type: str,
|
|
57
|
+
) -> Optional[tuple[type[ExecPlugin], type[object]]]:
|
|
58
|
+
"""Get the EE plugin class and stub class based on the plugin type."""
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
# pylint: disable-next=unused-argument
|
|
62
|
+
def add_ee_args_superexec(parser: argparse.ArgumentParser) -> None:
|
|
63
|
+
"""Add EE-specific arguments to the parser."""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def flower_superexec() -> None:
|
|
67
|
+
"""Run `flower-superexec` command."""
|
|
68
|
+
args = _parse_args().parse_args()
|
|
69
|
+
if not args.insecure:
|
|
70
|
+
flwr_exit(
|
|
71
|
+
ExitCode.COMMON_TLS_NOT_SUPPORTED,
|
|
72
|
+
"SuperExec does not support TLS yet.",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Log the first message after parsing arguments in case of `--help`
|
|
76
|
+
log(INFO, "Starting Flower SuperExec")
|
|
77
|
+
|
|
78
|
+
# Trigger telemetry event
|
|
79
|
+
event(EventType.RUN_SUPEREXEC_ENTER, {"plugin_type": args.plugin_type})
|
|
80
|
+
|
|
81
|
+
# Load plugin config from YAML file if provided
|
|
82
|
+
plugin_config = None
|
|
83
|
+
if plugin_config_path := getattr(args, "plugin_config", None):
|
|
84
|
+
try:
|
|
85
|
+
with open(plugin_config_path, encoding="utf-8") as file:
|
|
86
|
+
yaml_config: Optional[dict[str, Any]] = yaml.safe_load(file)
|
|
87
|
+
if yaml_config is None or EXEC_PLUGIN_SECTION not in yaml_config:
|
|
88
|
+
raise ValueError(f"Missing '{EXEC_PLUGIN_SECTION}' section.")
|
|
89
|
+
plugin_config = yaml_config[EXEC_PLUGIN_SECTION]
|
|
90
|
+
except (FileNotFoundError, yaml.YAMLError, ValueError) as e:
|
|
91
|
+
flwr_exit(
|
|
92
|
+
ExitCode.SUPEREXEC_INVALID_PLUGIN_CONFIG,
|
|
93
|
+
f"Failed to load plugin config from '{plugin_config_path}': {e!r}",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Get the plugin class and stub class based on the plugin type
|
|
97
|
+
plugin_class, stub_class = _get_plugin_and_stub_class(args.plugin_type)
|
|
98
|
+
run_superexec(
|
|
99
|
+
plugin_class=plugin_class,
|
|
100
|
+
stub_class=stub_class, # type: ignore
|
|
101
|
+
appio_api_address=args.appio_api_address,
|
|
102
|
+
plugin_config=plugin_config,
|
|
103
|
+
flwr_dir=args.flwr_dir,
|
|
104
|
+
parent_pid=args.parent_pid,
|
|
105
|
+
health_server_address=args.health_server_address,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _parse_args() -> argparse.ArgumentParser:
|
|
110
|
+
"""Parse `flower-superexec` command line arguments."""
|
|
111
|
+
parser = argparse.ArgumentParser(
|
|
112
|
+
description="Run Flower SuperExec.",
|
|
113
|
+
)
|
|
114
|
+
parser.add_argument(
|
|
115
|
+
"--appio-api-address", type=str, required=True, help="Address of the AppIO API"
|
|
116
|
+
)
|
|
117
|
+
parser.add_argument(
|
|
118
|
+
"--plugin-type",
|
|
119
|
+
type=str,
|
|
120
|
+
choices=ExecPluginType.all() + ExecEePluginType.all(),
|
|
121
|
+
required=True,
|
|
122
|
+
help="The type of plugin to use.",
|
|
123
|
+
)
|
|
124
|
+
parser.add_argument(
|
|
125
|
+
"--insecure",
|
|
126
|
+
action="store_true",
|
|
127
|
+
help="Connect to the AppIO API without TLS. "
|
|
128
|
+
"Data transmitted between the client and server is not encrypted. "
|
|
129
|
+
"Use this flag only if you understand the risks.",
|
|
130
|
+
)
|
|
131
|
+
parser.add_argument(
|
|
132
|
+
"--flwr-dir",
|
|
133
|
+
default=None,
|
|
134
|
+
help="""The path containing installed Flower Apps.
|
|
135
|
+
By default, this value is equal to:
|
|
136
|
+
|
|
137
|
+
- `$FLWR_HOME/` if `$FLWR_HOME` is defined
|
|
138
|
+
- `$XDG_DATA_HOME/.flwr/` if `$XDG_DATA_HOME` is defined
|
|
139
|
+
- `$HOME/.flwr/` in all other cases
|
|
140
|
+
""",
|
|
141
|
+
)
|
|
142
|
+
parser.add_argument(
|
|
143
|
+
"--parent-pid",
|
|
144
|
+
type=int,
|
|
145
|
+
default=None,
|
|
146
|
+
help="The PID of the parent process. When set, the process will terminate "
|
|
147
|
+
"when the parent process exits.",
|
|
148
|
+
)
|
|
149
|
+
add_ee_args_superexec(parser)
|
|
150
|
+
add_args_health(parser)
|
|
151
|
+
return parser
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _get_plugin_and_stub_class(
|
|
155
|
+
plugin_type: str,
|
|
156
|
+
) -> tuple[type[ExecPlugin], type[object]]:
|
|
157
|
+
"""Get the plugin class and stub class based on the plugin type."""
|
|
158
|
+
if plugin_type == ExecPluginType.CLIENT_APP:
|
|
159
|
+
return ClientAppExecPlugin, ClientAppIoStub
|
|
160
|
+
if plugin_type == ExecPluginType.SERVER_APP:
|
|
161
|
+
return ServerAppExecPlugin, ServerAppIoStub
|
|
162
|
+
if plugin_type == ExecPluginType.SIMULATION:
|
|
163
|
+
return SimulationExecPlugin, SimulationIoStub
|
|
164
|
+
if ret := get_ee_plugin_and_stub_class(plugin_type):
|
|
165
|
+
return ret # type: ignore[no-any-return]
|
|
166
|
+
raise ValueError(f"Unknown plugin type: {plugin_type}")
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
"""Constants for Flower infrastructure."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Top-level key in YAML config for exec plugin settings
|
|
19
|
+
EXEC_PLUGIN_SECTION = "exec_plugin"
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
# ==============================================================================
|
|
15
|
-
"""Flower
|
|
15
|
+
"""Flower CoreState."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
from .
|
|
18
|
+
from .corestate import CoreState
|
|
19
19
|
|
|
20
20
|
__all__ = [
|
|
21
|
-
"
|
|
21
|
+
"CoreState",
|
|
22
22
|
]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
"""Abstract base class CoreState."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from abc import ABC, abstractmethod
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CoreState(ABC):
|
|
23
|
+
"""Abstract base class for core state."""
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def create_token(self, run_id: int) -> Optional[str]:
|
|
27
|
+
"""Create a token for the given run ID.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
run_id : int
|
|
32
|
+
The ID of the run for which to create a token.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
str
|
|
37
|
+
The newly generated token if one does not already exist
|
|
38
|
+
for the given run ID, otherwise None.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def verify_token(self, run_id: int, token: str) -> bool:
|
|
43
|
+
"""Verify a token for the given run ID.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
run_id : int
|
|
48
|
+
The ID of the run for which to verify the token.
|
|
49
|
+
token : str
|
|
50
|
+
The token to verify.
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
bool
|
|
55
|
+
True if the token is valid for the run ID, False otherwise.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def delete_token(self, run_id: int) -> None:
|
|
60
|
+
"""Delete the token for the given run ID.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
run_id : int
|
|
65
|
+
The ID of the run for which to delete the token.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
@abstractmethod
|
|
69
|
+
def get_run_id_by_token(self, token: str) -> Optional[int]:
|
|
70
|
+
"""Get the run ID associated with a given token.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
token : str
|
|
75
|
+
The token to look up.
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
Optional[int]
|
|
80
|
+
The run ID if the token is valid, otherwise None.
|
|
81
|
+
"""
|
|
@@ -15,8 +15,11 @@
|
|
|
15
15
|
"""GRPC health servicers."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
from .health_server import add_args_health, run_health_server_grpc_no_tls
|
|
18
19
|
from .simple_health_servicer import SimpleHealthServicer
|
|
19
20
|
|
|
20
21
|
__all__ = [
|
|
21
22
|
"SimpleHealthServicer",
|
|
23
|
+
"add_args_health",
|
|
24
|
+
"run_health_server_grpc_no_tls",
|
|
22
25
|
]
|
|
@@ -0,0 +1,53 @@
|
|
|
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
|
+
"""Health servers."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
from logging import INFO
|
|
20
|
+
|
|
21
|
+
import grpc
|
|
22
|
+
from grpc_health.v1.health_pb2_grpc import add_HealthServicer_to_server
|
|
23
|
+
|
|
24
|
+
from flwr.common.grpc import generic_create_grpc_server
|
|
25
|
+
from flwr.common.logger import log
|
|
26
|
+
|
|
27
|
+
from .simple_health_servicer import SimpleHealthServicer
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def run_health_server_grpc_no_tls(address: str) -> grpc.Server:
|
|
31
|
+
"""Run gRPC health server with no TLS."""
|
|
32
|
+
health_server = generic_create_grpc_server(
|
|
33
|
+
servicer_and_add_fn=(
|
|
34
|
+
SimpleHealthServicer(),
|
|
35
|
+
add_HealthServicer_to_server,
|
|
36
|
+
),
|
|
37
|
+
server_address=address,
|
|
38
|
+
certificates=None,
|
|
39
|
+
)
|
|
40
|
+
log(INFO, "Starting gRPC health server on %s", address)
|
|
41
|
+
health_server.start()
|
|
42
|
+
return health_server
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def add_args_health(parser: argparse.ArgumentParser) -> None:
|
|
46
|
+
"""Add arguments for health server."""
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--health-server-address",
|
|
49
|
+
type=str,
|
|
50
|
+
default=None,
|
|
51
|
+
help="Health service gRPC server address (IPv4, IPv6, or a domain name) "
|
|
52
|
+
"with no TLS. If not set, the health server will not be started.",
|
|
53
|
+
)
|
|
@@ -28,11 +28,11 @@ class SimpleHealthServicer(HealthServicer): # type: ignore
|
|
|
28
28
|
"""A simple gRPC health servicer that always returns SERVING."""
|
|
29
29
|
|
|
30
30
|
def Check(
|
|
31
|
-
self, request: HealthCheckRequest, context: grpc.
|
|
31
|
+
self, request: HealthCheckRequest, context: grpc.ServicerContext
|
|
32
32
|
) -> HealthCheckResponse:
|
|
33
33
|
"""Return a HealthCheckResponse with SERVING status."""
|
|
34
34
|
return HealthCheckResponse(status=HealthCheckResponse.SERVING)
|
|
35
35
|
|
|
36
|
-
def Watch(self, request: HealthCheckRequest, context: grpc.
|
|
36
|
+
def Watch(self, request: HealthCheckRequest, context: grpc.ServicerContext) -> None:
|
|
37
37
|
"""Watch the health status (not implemented)."""
|
|
38
38
|
context.abort(grpc.StatusCode.UNIMPLEMENTED, "Watch is not implemented")
|
|
@@ -0,0 +1,28 @@
|
|
|
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 SuperExec plugins."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from .clientapp_exec_plugin import ClientAppExecPlugin
|
|
19
|
+
from .exec_plugin import ExecPlugin
|
|
20
|
+
from .serverapp_exec_plugin import ServerAppExecPlugin
|
|
21
|
+
from .simulation_exec_plugin import SimulationExecPlugin
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"ClientAppExecPlugin",
|
|
25
|
+
"ExecPlugin",
|
|
26
|
+
"ServerAppExecPlugin",
|
|
27
|
+
"SimulationExecPlugin",
|
|
28
|
+
]
|
|
@@ -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
|
-
"""Simple Flower
|
|
15
|
+
"""Simple base Flower SuperExec plugin for app processes."""
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
import os
|
|
@@ -20,15 +20,19 @@ import subprocess
|
|
|
20
20
|
from collections.abc import Sequence
|
|
21
21
|
from typing import Optional
|
|
22
22
|
|
|
23
|
-
from
|
|
23
|
+
from .exec_plugin import ExecPlugin
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
class
|
|
27
|
-
"""Simple Flower
|
|
26
|
+
class BaseExecPlugin(ExecPlugin):
|
|
27
|
+
"""Simple Flower SuperExec plugin for app processes.
|
|
28
28
|
|
|
29
29
|
The plugin always selects the first candidate run ID.
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
|
+
# Placeholders to be defined in subclasses
|
|
33
|
+
command = ""
|
|
34
|
+
appio_api_address_arg = ""
|
|
35
|
+
|
|
32
36
|
def select_run_id(self, candidate_run_ids: Sequence[int]) -> Optional[int]:
|
|
33
37
|
"""Select a run ID to execute from a sequence of candidates."""
|
|
34
38
|
if not candidate_run_ids:
|
|
@@ -37,8 +41,8 @@ class SimpleClientAppSchedulerPlugin(SchedulerPlugin):
|
|
|
37
41
|
|
|
38
42
|
def launch_app(self, token: str, run_id: int) -> None:
|
|
39
43
|
"""Launch the application associated with a given run ID and token."""
|
|
40
|
-
cmds = [
|
|
41
|
-
cmds += [
|
|
44
|
+
cmds = [self.command, "--insecure"]
|
|
45
|
+
cmds += [self.appio_api_address_arg, self.appio_api_address]
|
|
42
46
|
cmds += ["--token", token]
|
|
43
47
|
cmds += ["--parent-pid", str(os.getpid())]
|
|
44
48
|
cmds += ["--flwr-dir", self.flwr_dir]
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
"""Simple Flower SuperExec plugin for ClientApp."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from .base_exec_plugin import BaseExecPlugin
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ClientAppExecPlugin(BaseExecPlugin):
|
|
22
|
+
"""Simple Flower SuperExec plugin for ClientApp.
|
|
23
|
+
|
|
24
|
+
The plugin always selects the first candidate run ID.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
command = "flwr-clientapp"
|
|
28
|
+
appio_api_address_arg = "--clientappio-api-address"
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
# ==============================================================================
|
|
15
|
-
"""Abstract base class
|
|
15
|
+
"""Abstract base class ExecPlugin."""
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
from abc import ABC, abstractmethod
|
|
19
19
|
from collections.abc import Sequence
|
|
20
|
-
from typing import Callable, Optional
|
|
20
|
+
from typing import Any, Callable, Optional
|
|
21
21
|
|
|
22
22
|
from flwr.common.typing import Run
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class
|
|
26
|
-
"""Abstract base class for
|
|
25
|
+
class ExecPlugin(ABC):
|
|
26
|
+
"""Abstract base class for SuperExec plugins."""
|
|
27
27
|
|
|
28
28
|
def __init__(
|
|
29
29
|
self,
|
|
@@ -59,7 +59,7 @@ class SchedulerPlugin(ABC):
|
|
|
59
59
|
|
|
60
60
|
This method starts the application process using the given `token`.
|
|
61
61
|
The `run_id` is used solely for bookkeeping purposes, allowing any
|
|
62
|
-
|
|
62
|
+
plugin implementation to associate this launch with a specific run.
|
|
63
63
|
|
|
64
64
|
Parameters
|
|
65
65
|
----------
|
|
@@ -69,3 +69,13 @@ class SchedulerPlugin(ABC):
|
|
|
69
69
|
The ID of the run associated with the token, used for tracking or
|
|
70
70
|
logging purposes.
|
|
71
71
|
"""
|
|
72
|
+
|
|
73
|
+
# This method is optional to implement
|
|
74
|
+
def load_config(self, yaml_config: dict[str, Any]) -> None:
|
|
75
|
+
"""Load configuration from a YAML dictionary.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
yaml_config : dict[str, Any]
|
|
80
|
+
A dictionary representing the YAML configuration.
|
|
81
|
+
"""
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
"""Simple Flower SuperExec plugin for ServerApp."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from .base_exec_plugin import BaseExecPlugin
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ServerAppExecPlugin(BaseExecPlugin):
|
|
22
|
+
"""Simple Flower SuperExec plugin for ServerApp.
|
|
23
|
+
|
|
24
|
+
The plugin always selects the first candidate run ID.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
command = "flwr-serverapp"
|
|
28
|
+
appio_api_address_arg = "--serverappio-api-address"
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
"""Simple Flower SuperExec plugin for simulation processes."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from .base_exec_plugin import BaseExecPlugin
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SimulationExecPlugin(BaseExecPlugin):
|
|
22
|
+
"""Simple Flower SuperExec plugin for simulation processes.
|
|
23
|
+
|
|
24
|
+
The plugin always selects the first candidate run ID.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
command = "flwr-simulation"
|
|
28
|
+
appio_api_address_arg = "--simulationio-api-address"
|