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.
Files changed (182) hide show
  1. flwr/__init__.py +4 -1
  2. flwr/app/__init__.py +28 -0
  3. flwr/app/exception.py +31 -0
  4. flwr/cli/app.py +2 -0
  5. flwr/cli/auth_plugin/oidc_cli_plugin.py +4 -4
  6. flwr/cli/cli_user_auth_interceptor.py +1 -1
  7. flwr/cli/config_utils.py +3 -3
  8. flwr/cli/constant.py +25 -8
  9. flwr/cli/log.py +9 -9
  10. flwr/cli/login/login.py +3 -3
  11. flwr/cli/ls.py +5 -5
  12. flwr/cli/new/new.py +15 -2
  13. flwr/cli/new/templates/app/README.flowertune.md.tpl +1 -1
  14. flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +1 -0
  15. flwr/cli/new/templates/app/code/client.baseline.py.tpl +64 -47
  16. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +68 -30
  17. flwr/cli/new/templates/app/code/client.jax.py.tpl +63 -42
  18. flwr/cli/new/templates/app/code/client.mlx.py.tpl +80 -51
  19. flwr/cli/new/templates/app/code/client.numpy.py.tpl +36 -13
  20. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +71 -46
  21. flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +55 -0
  22. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +75 -30
  23. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +69 -44
  24. flwr/cli/new/templates/app/code/client.xgboost.py.tpl +110 -0
  25. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +56 -90
  26. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +1 -23
  27. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +37 -58
  28. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +39 -44
  29. flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -14
  30. flwr/cli/new/templates/app/code/server.baseline.py.tpl +27 -29
  31. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +23 -19
  32. flwr/cli/new/templates/app/code/server.jax.py.tpl +27 -14
  33. flwr/cli/new/templates/app/code/server.mlx.py.tpl +29 -19
  34. flwr/cli/new/templates/app/code/server.numpy.py.tpl +30 -17
  35. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +36 -26
  36. flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +31 -0
  37. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +29 -21
  38. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +28 -19
  39. flwr/cli/new/templates/app/code/server.xgboost.py.tpl +56 -0
  40. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +16 -20
  41. flwr/cli/new/templates/app/code/task.jax.py.tpl +1 -1
  42. flwr/cli/new/templates/app/code/task.numpy.py.tpl +1 -1
  43. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +14 -27
  44. flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +111 -0
  45. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +1 -2
  46. flwr/cli/new/templates/app/code/task.xgboost.py.tpl +67 -0
  47. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
  48. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +2 -2
  49. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
  50. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
  51. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +2 -2
  52. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
  53. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +3 -3
  54. flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +53 -0
  55. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
  56. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
  57. flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +61 -0
  58. flwr/cli/pull.py +100 -0
  59. flwr/cli/run/run.py +9 -13
  60. flwr/cli/stop.py +7 -4
  61. flwr/cli/utils.py +36 -8
  62. flwr/client/grpc_rere_client/connection.py +1 -12
  63. flwr/client/rest_client/connection.py +3 -0
  64. flwr/clientapp/__init__.py +10 -0
  65. flwr/clientapp/mod/__init__.py +29 -0
  66. flwr/clientapp/mod/centraldp_mods.py +248 -0
  67. flwr/clientapp/mod/localdp_mod.py +169 -0
  68. flwr/clientapp/typing.py +22 -0
  69. flwr/common/args.py +20 -6
  70. flwr/common/auth_plugin/__init__.py +4 -4
  71. flwr/common/auth_plugin/auth_plugin.py +7 -7
  72. flwr/common/constant.py +26 -4
  73. flwr/common/event_log_plugin/event_log_plugin.py +1 -1
  74. flwr/common/exit/__init__.py +4 -0
  75. flwr/common/exit/exit.py +8 -1
  76. flwr/common/exit/exit_code.py +30 -7
  77. flwr/common/exit/exit_handler.py +62 -0
  78. flwr/common/{exit_handlers.py → exit/signal_handler.py} +20 -37
  79. flwr/common/grpc.py +0 -11
  80. flwr/common/inflatable_utils.py +1 -1
  81. flwr/common/logger.py +1 -1
  82. flwr/common/record/typeddict.py +12 -0
  83. flwr/common/retry_invoker.py +30 -11
  84. flwr/common/telemetry.py +4 -0
  85. flwr/compat/server/app.py +2 -2
  86. flwr/proto/appio_pb2.py +25 -17
  87. flwr/proto/appio_pb2.pyi +46 -2
  88. flwr/proto/clientappio_pb2.py +3 -11
  89. flwr/proto/clientappio_pb2.pyi +0 -47
  90. flwr/proto/clientappio_pb2_grpc.py +19 -20
  91. flwr/proto/clientappio_pb2_grpc.pyi +10 -11
  92. flwr/proto/control_pb2.py +66 -0
  93. flwr/proto/{exec_pb2.pyi → control_pb2.pyi} +24 -0
  94. flwr/proto/{exec_pb2_grpc.py → control_pb2_grpc.py} +88 -54
  95. flwr/proto/control_pb2_grpc.pyi +106 -0
  96. flwr/proto/serverappio_pb2.py +2 -2
  97. flwr/proto/serverappio_pb2_grpc.py +68 -0
  98. flwr/proto/serverappio_pb2_grpc.pyi +26 -0
  99. flwr/proto/simulationio_pb2.py +4 -11
  100. flwr/proto/simulationio_pb2.pyi +0 -58
  101. flwr/proto/simulationio_pb2_grpc.py +129 -27
  102. flwr/proto/simulationio_pb2_grpc.pyi +52 -13
  103. flwr/server/app.py +142 -152
  104. flwr/server/grid/grpc_grid.py +3 -0
  105. flwr/server/grid/inmemory_grid.py +1 -0
  106. flwr/server/serverapp/app.py +157 -146
  107. flwr/server/superlink/fleet/vce/backend/raybackend.py +3 -1
  108. flwr/server/superlink/fleet/vce/vce_api.py +6 -6
  109. flwr/server/superlink/linkstate/in_memory_linkstate.py +34 -0
  110. flwr/server/superlink/linkstate/linkstate.py +2 -1
  111. flwr/server/superlink/linkstate/sqlite_linkstate.py +45 -0
  112. flwr/server/superlink/serverappio/serverappio_grpc.py +1 -1
  113. flwr/server/superlink/serverappio/serverappio_servicer.py +61 -6
  114. flwr/server/superlink/simulation/simulationio_servicer.py +97 -21
  115. flwr/serverapp/__init__.py +12 -0
  116. flwr/serverapp/exception.py +38 -0
  117. flwr/serverapp/strategy/__init__.py +64 -0
  118. flwr/serverapp/strategy/bulyan.py +238 -0
  119. flwr/serverapp/strategy/dp_adaptive_clipping.py +335 -0
  120. flwr/serverapp/strategy/dp_fixed_clipping.py +374 -0
  121. flwr/serverapp/strategy/fedadagrad.py +159 -0
  122. flwr/serverapp/strategy/fedadam.py +178 -0
  123. flwr/serverapp/strategy/fedavg.py +320 -0
  124. flwr/serverapp/strategy/fedavgm.py +198 -0
  125. flwr/serverapp/strategy/fedmedian.py +105 -0
  126. flwr/serverapp/strategy/fedopt.py +218 -0
  127. flwr/serverapp/strategy/fedprox.py +174 -0
  128. flwr/serverapp/strategy/fedtrimmedavg.py +176 -0
  129. flwr/serverapp/strategy/fedxgb_bagging.py +117 -0
  130. flwr/serverapp/strategy/fedxgb_cyclic.py +220 -0
  131. flwr/serverapp/strategy/fedyogi.py +170 -0
  132. flwr/serverapp/strategy/krum.py +112 -0
  133. flwr/serverapp/strategy/multikrum.py +247 -0
  134. flwr/serverapp/strategy/qfedavg.py +252 -0
  135. flwr/serverapp/strategy/result.py +105 -0
  136. flwr/serverapp/strategy/strategy.py +285 -0
  137. flwr/serverapp/strategy/strategy_utils.py +299 -0
  138. flwr/simulation/app.py +161 -164
  139. flwr/simulation/run_simulation.py +25 -30
  140. flwr/supercore/app_utils.py +58 -0
  141. flwr/{supernode/scheduler → supercore/cli}/__init__.py +3 -3
  142. flwr/supercore/cli/flower_superexec.py +166 -0
  143. flwr/supercore/constant.py +19 -0
  144. flwr/supercore/{scheduler → corestate}/__init__.py +3 -3
  145. flwr/supercore/corestate/corestate.py +81 -0
  146. flwr/supercore/grpc_health/__init__.py +3 -0
  147. flwr/supercore/grpc_health/health_server.py +53 -0
  148. flwr/supercore/grpc_health/simple_health_servicer.py +2 -2
  149. flwr/{superexec → supercore/superexec}/__init__.py +1 -1
  150. flwr/supercore/superexec/plugin/__init__.py +28 -0
  151. flwr/{supernode/scheduler/simple_clientapp_scheduler_plugin.py → supercore/superexec/plugin/base_exec_plugin.py} +10 -6
  152. flwr/supercore/superexec/plugin/clientapp_exec_plugin.py +28 -0
  153. flwr/supercore/{scheduler/plugin.py → superexec/plugin/exec_plugin.py} +15 -5
  154. flwr/supercore/superexec/plugin/serverapp_exec_plugin.py +28 -0
  155. flwr/supercore/superexec/plugin/simulation_exec_plugin.py +28 -0
  156. flwr/supercore/superexec/run_superexec.py +199 -0
  157. flwr/superlink/artifact_provider/__init__.py +22 -0
  158. flwr/superlink/artifact_provider/artifact_provider.py +37 -0
  159. flwr/superlink/servicer/__init__.py +15 -0
  160. flwr/superlink/servicer/control/__init__.py +22 -0
  161. flwr/{superexec/exec_event_log_interceptor.py → superlink/servicer/control/control_event_log_interceptor.py} +7 -7
  162. flwr/{superexec/exec_grpc.py → superlink/servicer/control/control_grpc.py} +27 -29
  163. flwr/{superexec/exec_license_interceptor.py → superlink/servicer/control/control_license_interceptor.py} +6 -6
  164. flwr/{superexec/exec_servicer.py → superlink/servicer/control/control_servicer.py} +127 -31
  165. flwr/{superexec/exec_user_auth_interceptor.py → superlink/servicer/control/control_user_auth_interceptor.py} +10 -10
  166. flwr/supernode/cli/flower_supernode.py +3 -0
  167. flwr/supernode/cli/flwr_clientapp.py +18 -21
  168. flwr/supernode/nodestate/in_memory_nodestate.py +2 -2
  169. flwr/supernode/nodestate/nodestate.py +3 -59
  170. flwr/supernode/runtime/run_clientapp.py +39 -102
  171. flwr/supernode/servicer/clientappio/clientappio_servicer.py +10 -17
  172. flwr/supernode/start_client_internal.py +35 -76
  173. {flwr-1.20.0.dist-info → flwr-1.22.0.dist-info}/METADATA +9 -18
  174. {flwr-1.20.0.dist-info → flwr-1.22.0.dist-info}/RECORD +176 -128
  175. {flwr-1.20.0.dist-info → flwr-1.22.0.dist-info}/entry_points.txt +1 -0
  176. flwr/proto/exec_pb2.py +0 -62
  177. flwr/proto/exec_pb2_grpc.pyi +0 -93
  178. flwr/superexec/app.py +0 -45
  179. flwr/superexec/deployment.py +0 -191
  180. flwr/superexec/executor.py +0 -100
  181. flwr/superexec/simulation.py +0 -129
  182. {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 App Scheduler."""
15
+ """Flower CoreState."""
16
16
 
17
17
 
18
- from .plugin import SchedulerPlugin
18
+ from .corestate import CoreState
19
19
 
20
20
  __all__ = [
21
- "SchedulerPlugin",
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.RpcContext
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.RpcContext) -> None:
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")
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
- """Flower SuperExec service."""
15
+ """Flower SuperExec."""
@@ -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 ClientApp Scheduler plugin."""
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 flwr.supercore.scheduler import SchedulerPlugin
23
+ from .exec_plugin import ExecPlugin
24
24
 
25
25
 
26
- class SimpleClientAppSchedulerPlugin(SchedulerPlugin):
27
- """Simple Flower ClientApp Scheduler plugin.
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 = ["flwr-clientapp", "--insecure"]
41
- cmds += ["--clientappio-api-address", self.appio_api_address]
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 SchedulerPlugin."""
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 SchedulerPlugin(ABC):
26
- """Abstract base class for Scheduler plugins."""
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
- scheduler implementation to associate this launch with a specific run.
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"