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,199 @@
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."""
16
+
17
+
18
+ import time
19
+ from logging import WARN
20
+ from typing import Any, Optional, Union
21
+
22
+ from flwr.common.config import get_flwr_dir
23
+ from flwr.common.exit import ExitCode, flwr_exit, register_signal_handlers
24
+ from flwr.common.grpc import create_channel, on_channel_state_change
25
+ from flwr.common.logger import log
26
+ from flwr.common.retry_invoker import _make_simple_grpc_retry_invoker, _wrap_stub
27
+ from flwr.common.serde import run_from_proto
28
+ from flwr.common.telemetry import EventType
29
+ from flwr.common.typing import Run
30
+ from flwr.proto.appio_pb2 import ( # pylint: disable=E0611
31
+ ListAppsToLaunchRequest,
32
+ RequestTokenRequest,
33
+ )
34
+ from flwr.proto.clientappio_pb2_grpc import ClientAppIoStub
35
+ from flwr.proto.run_pb2 import GetRunRequest # pylint: disable=E0611
36
+ from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub
37
+ from flwr.proto.simulationio_pb2_grpc import SimulationIoStub
38
+ from flwr.supercore.app_utils import start_parent_process_monitor
39
+ from flwr.supercore.grpc_health import run_health_server_grpc_no_tls
40
+
41
+ from .plugin import ExecPlugin
42
+
43
+
44
+ def run_superexec( # pylint: disable=R0913,R0914,R0917
45
+ plugin_class: type[ExecPlugin],
46
+ stub_class: Union[
47
+ type[ClientAppIoStub], type[ServerAppIoStub], type[SimulationIoStub]
48
+ ],
49
+ appio_api_address: str,
50
+ plugin_config: Optional[dict[str, Any]] = None,
51
+ flwr_dir: Optional[str] = None,
52
+ parent_pid: Optional[int] = None,
53
+ health_server_address: Optional[str] = None,
54
+ ) -> None:
55
+ """Run Flower SuperExec.
56
+
57
+ Parameters
58
+ ----------
59
+ plugin_class : type[ExecPlugin]
60
+ The class of the SuperExec plugin to use.
61
+ stub_class : type[ClientAppIoStub]
62
+ The gRPC stub class for the AppIO API.
63
+ appio_api_address : str
64
+ The address of the AppIO API.
65
+ plugin_config : Optional[dict[str, Any]] (default: None)
66
+ The configuration dictionary for the plugin. If `None`, the plugin will use
67
+ its default configuration.
68
+ flwr_dir : Optional[str] (default: None)
69
+ The Flower directory.
70
+ parent_pid : Optional[int] (default: None)
71
+ The PID of the parent process. If provided, the SuperExec will terminate
72
+ when the parent process exits.
73
+ health_server_address : Optional[str] (default: None)
74
+ The address of the health server. If `None` is provided, the health server will
75
+ NOT be started.
76
+ """
77
+ # Start monitoring the parent process if a PID is provided
78
+ if parent_pid is not None:
79
+ start_parent_process_monitor(parent_pid)
80
+
81
+ # Launch gRPC health server
82
+ grpc_servers = []
83
+ if health_server_address is not None:
84
+ health_server = run_health_server_grpc_no_tls(health_server_address)
85
+ grpc_servers.append(health_server)
86
+
87
+ # Create the channel to the AppIO API
88
+ # No TLS support for now, so insecure connection
89
+ channel = create_channel(
90
+ server_address=appio_api_address,
91
+ insecure=True,
92
+ root_certificates=None,
93
+ )
94
+ channel.subscribe(on_channel_state_change)
95
+
96
+ # Register exit handlers to close the channel on exit
97
+ register_signal_handlers(
98
+ event_type=EventType.RUN_SUPEREXEC_LEAVE,
99
+ exit_message="SuperExec terminated gracefully.",
100
+ grpc_servers=grpc_servers,
101
+ exit_handlers=[lambda: channel.close()], # pylint: disable=W0108
102
+ )
103
+
104
+ # Create the gRPC stub for the AppIO API
105
+ stub = stub_class(channel)
106
+ _wrap_stub(stub, _make_simple_grpc_retry_invoker())
107
+
108
+ def get_run(run_id: int) -> Run:
109
+ _req = GetRunRequest(run_id=run_id)
110
+ _res = stub.GetRun(_req)
111
+ return run_from_proto(_res.run)
112
+
113
+ # Create the SuperExec plugin instance
114
+ plugin = plugin_class(
115
+ appio_api_address=appio_api_address,
116
+ flwr_dir=str(get_flwr_dir(flwr_dir)),
117
+ get_run=get_run,
118
+ )
119
+
120
+ # Load plugin configuration from file if provided
121
+ try:
122
+ if plugin_config is not None:
123
+ plugin.load_config(plugin_config)
124
+ except (KeyError, ValueError) as e:
125
+ flwr_exit(
126
+ code=ExitCode.SUPEREXEC_INVALID_PLUGIN_CONFIG,
127
+ message=f"Invalid plugin config: {e!r}",
128
+ )
129
+
130
+ # Start the main loop
131
+ try:
132
+ while True:
133
+ # Fetch suitable run IDs
134
+ ls_req = ListAppsToLaunchRequest()
135
+ ls_res = stub.ListAppsToLaunch(ls_req)
136
+
137
+ # Allow the plugin to select a run ID
138
+ run_id = None
139
+ if ls_res.run_ids:
140
+ run_id = plugin.select_run_id(candidate_run_ids=ls_res.run_ids)
141
+
142
+ # Apply for a token if a run ID was selected
143
+ if run_id is not None:
144
+ tk_req = RequestTokenRequest(run_id=run_id)
145
+ tk_res = stub.RequestToken(tk_req)
146
+
147
+ # Launch the app if a token was granted; do nothing if not
148
+ if tk_res.token:
149
+ plugin.launch_app(token=tk_res.token, run_id=run_id)
150
+
151
+ # Sleep for a while before checking again
152
+ time.sleep(1)
153
+ finally:
154
+ channel.close()
155
+
156
+
157
+ def run_with_deprecation_warning( # pylint: disable=R0913, R0917
158
+ cmd: str,
159
+ plugin_type: str,
160
+ plugin_class: type[ExecPlugin],
161
+ stub_class: Union[
162
+ type[ClientAppIoStub], type[ServerAppIoStub], type[SimulationIoStub]
163
+ ],
164
+ appio_api_address: str,
165
+ flwr_dir: Optional[str],
166
+ parent_pid: Optional[int],
167
+ warn_run_once: bool,
168
+ ) -> None:
169
+ """Log a deprecation warning and run the equivalent `flower-superexec` command.
170
+
171
+ Used for legacy long-running `flwr-*` commands (i.e., without `--token`) that will
172
+ be removed in favor of `flower-superexec`.
173
+ """
174
+ log(
175
+ WARN,
176
+ "Directly executing `%s` is DEPRECATED and will be prohibited "
177
+ "in a future release. Please use `flower-superexec` instead.",
178
+ cmd,
179
+ )
180
+ log(WARN, "For now, the following command is being run automatically:")
181
+ new_cmd = f"flower-superexec --insecure --plugin-type {plugin_type} "
182
+ new_cmd += f"--appio-api-address {appio_api_address} "
183
+ if flwr_dir is not None:
184
+ new_cmd += f"--flwr-dir {flwr_dir} "
185
+ if parent_pid is not None:
186
+ new_cmd += f"--parent-pid {parent_pid}"
187
+ log(WARN, new_cmd)
188
+
189
+ # Warn about unsupported `--run-once` flag
190
+ if warn_run_once:
191
+ log(WARN, "`flower-superexec` does not support the `--run-once` flag.")
192
+
193
+ run_superexec(
194
+ plugin_class=plugin_class,
195
+ stub_class=stub_class,
196
+ appio_api_address=appio_api_address,
197
+ flwr_dir=flwr_dir,
198
+ parent_pid=parent_pid,
199
+ )
@@ -0,0 +1,22 @@
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
+ """ArtifactProvider for SuperLink."""
16
+
17
+
18
+ from .artifact_provider import ArtifactProvider
19
+
20
+ __all__ = [
21
+ "ArtifactProvider",
22
+ ]
@@ -0,0 +1,37 @@
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 for ArtifactProvider."""
16
+
17
+
18
+ from abc import ABC, abstractmethod
19
+ from typing import Optional
20
+
21
+
22
+ class ArtifactProvider(ABC):
23
+ """ArtifactProvider interface for providing artifact download links."""
24
+
25
+ @abstractmethod
26
+ def get_url(self, run_id: int) -> Optional[str]:
27
+ """Return the artifact download link for the given run ID."""
28
+
29
+ @property
30
+ @abstractmethod
31
+ def output_dir(self) -> str:
32
+ """Permanent storage directory."""
33
+
34
+ @property
35
+ @abstractmethod
36
+ def tmp_dir(self) -> str:
37
+ """Temporary storage directory."""
@@ -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 servicers."""
@@ -0,0 +1,22 @@
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
+ """Control API Servicer."""
16
+
17
+
18
+ from .control_grpc import run_control_api_grpc
19
+
20
+ __all__ = [
21
+ "run_control_api_grpc",
22
+ ]
@@ -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
- """Flower Exec API event log interceptor."""
15
+ """Flower Control API event log interceptor."""
16
16
 
17
17
 
18
18
  from collections.abc import Iterator
@@ -24,11 +24,11 @@ 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 shared_account_info
27
+ from .control_user_auth_interceptor import shared_account_info
28
28
 
29
29
 
30
- class ExecEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
31
- """Exec API interceptor for logging events."""
30
+ class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
31
+ """Control API interceptor for logging events."""
32
32
 
33
33
  def __init__(self, log_plugin: EventLogWriterPlugin) -> None:
34
34
  self.log_plugin = log_plugin
@@ -44,12 +44,12 @@ class ExecEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
44
44
  Continue RPC call if event logger is enabled on the SuperLink, else, terminate
45
45
  RPC call by setting context to abort.
46
46
  """
47
- # Only apply to Exec service
48
- if not handler_call_details.method.startswith("/flwr.proto.Exec/"):
47
+ # Only apply to Control service
48
+ if not handler_call_details.method.startswith("/flwr.proto.Control/"):
49
49
  return continuation(handler_call_details)
50
50
 
51
51
  # One of the method handlers in
52
- # `flwr.superexec.exec_servicer.ExecServicer`
52
+ # `flwr.superlink.servicer.control.ControlServicer`
53
53
  method_handler: grpc.RpcMethodHandler = continuation(handler_call_details)
54
54
  method_name: str = handler_call_details.method
55
55
  return self._generic_event_log_unary_method_handler(method_handler, method_name)
@@ -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
- """SuperExec gRPC API."""
15
+ """Control API server."""
16
16
 
17
17
 
18
18
  from logging import INFO
@@ -21,23 +21,22 @@ 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, ExecAuthzPlugin
24
+ from flwr.common.auth_plugin import ControlAuthPlugin, ControlAuthzPlugin
25
25
  from flwr.common.event_log_plugin import EventLogWriterPlugin
26
26
  from flwr.common.exit import ExitCode, flwr_exit
27
27
  from flwr.common.grpc import generic_create_grpc_server
28
28
  from flwr.common.logger import log
29
- from flwr.common.typing import UserConfig
30
- from flwr.proto.exec_pb2_grpc import add_ExecServicer_to_server
29
+ from flwr.proto.control_pb2_grpc import add_ControlServicer_to_server
31
30
  from flwr.server.superlink.linkstate import LinkStateFactory
32
31
  from flwr.supercore.ffs import FfsFactory
33
32
  from flwr.supercore.license_plugin import LicensePlugin
34
33
  from flwr.supercore.object_store import ObjectStoreFactory
35
- from flwr.superexec.exec_event_log_interceptor import ExecEventLogInterceptor
36
- from flwr.superexec.exec_license_interceptor import ExecLicenseInterceptor
37
- from flwr.superexec.exec_user_auth_interceptor import ExecUserAuthInterceptor
34
+ from flwr.superlink.artifact_provider import ArtifactProvider
38
35
 
39
- from .exec_servicer import ExecServicer
40
- from .executor import Executor
36
+ from .control_event_log_interceptor import ControlEventLogInterceptor
37
+ from .control_license_interceptor import ControlLicenseInterceptor
38
+ from .control_servicer import ControlServicer
39
+ from .control_user_auth_interceptor import ControlUserAuthInterceptor
41
40
 
42
41
  try:
43
42
  from flwr.ee import get_license_plugin
@@ -48,44 +47,43 @@ except ImportError:
48
47
 
49
48
 
50
49
  # pylint: disable-next=too-many-arguments,too-many-positional-arguments,too-many-locals
51
- def run_exec_api_grpc(
50
+ def run_control_api_grpc(
52
51
  address: str,
53
- executor: Executor,
54
52
  state_factory: LinkStateFactory,
55
53
  ffs_factory: FfsFactory,
56
54
  objectstore_factory: ObjectStoreFactory,
57
55
  certificates: Optional[tuple[bytes, bytes, bytes]],
58
- config: UserConfig,
59
- auth_plugin: Optional[ExecAuthPlugin] = None,
60
- authz_plugin: Optional[ExecAuthzPlugin] = None,
56
+ is_simulation: bool,
57
+ auth_plugin: Optional[ControlAuthPlugin] = None,
58
+ authz_plugin: Optional[ControlAuthzPlugin] = None,
61
59
  event_log_plugin: Optional[EventLogWriterPlugin] = None,
60
+ artifact_provider: Optional[ArtifactProvider] = None,
62
61
  ) -> grpc.Server:
63
- """Run Exec API (gRPC, request-response)."""
64
- executor.set_config(config)
65
-
62
+ """Run Control API (gRPC, request-response)."""
66
63
  license_plugin: Optional[LicensePlugin] = get_license_plugin()
67
64
  if license_plugin and not license_plugin.check_license():
68
65
  flwr_exit(ExitCode.SUPERLINK_LICENSE_INVALID)
69
66
 
70
- exec_servicer: grpc.Server = ExecServicer(
67
+ control_servicer: grpc.Server = ControlServicer(
71
68
  linkstate_factory=state_factory,
72
69
  ffs_factory=ffs_factory,
73
70
  objectstore_factory=objectstore_factory,
74
- executor=executor,
71
+ is_simulation=is_simulation,
75
72
  auth_plugin=auth_plugin,
73
+ artifact_provider=artifact_provider,
76
74
  )
77
75
  interceptors: list[grpc.ServerInterceptor] = []
78
76
  if license_plugin is not None:
79
- interceptors.append(ExecLicenseInterceptor(license_plugin))
77
+ interceptors.append(ControlLicenseInterceptor(license_plugin))
80
78
  if auth_plugin is not None and authz_plugin is not None:
81
- interceptors.append(ExecUserAuthInterceptor(auth_plugin, authz_plugin))
79
+ interceptors.append(ControlUserAuthInterceptor(auth_plugin, authz_plugin))
82
80
  # Event log interceptor must be added after user auth interceptor
83
81
  if event_log_plugin is not None:
84
- interceptors.append(ExecEventLogInterceptor(event_log_plugin))
82
+ interceptors.append(ControlEventLogInterceptor(event_log_plugin))
85
83
  log(INFO, "Flower event logging enabled")
86
- exec_add_servicer_to_server_fn = add_ExecServicer_to_server
87
- exec_grpc_server = generic_create_grpc_server(
88
- servicer_and_add_fn=(exec_servicer, exec_add_servicer_to_server_fn),
84
+ control_add_servicer_to_server_fn = add_ControlServicer_to_server
85
+ control_grpc_server = generic_create_grpc_server(
86
+ servicer_and_add_fn=(control_servicer, control_add_servicer_to_server_fn),
89
87
  server_address=address,
90
88
  max_message_length=GRPC_MAX_MESSAGE_LENGTH,
91
89
  certificates=certificates,
@@ -93,14 +91,14 @@ def run_exec_api_grpc(
93
91
  )
94
92
 
95
93
  if auth_plugin is None:
96
- log(INFO, "Flower Deployment Engine: Starting Exec API on %s", address)
94
+ log(INFO, "Flower Deployment Runtime: Starting Control API on %s", address)
97
95
  else:
98
96
  log(
99
97
  INFO,
100
- "Flower Deployment Engine: Starting Exec API with user "
98
+ "Flower Deployment Runtime: Starting Control API with user "
101
99
  "authentication on %s",
102
100
  address,
103
101
  )
104
- exec_grpc_server.start()
102
+ control_grpc_server.start()
105
103
 
106
- return exec_grpc_server
104
+ return control_grpc_server
@@ -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
- """Flower Exec API license interceptor."""
15
+ """Flower Control API license interceptor."""
16
16
 
17
17
 
18
18
  from collections.abc import Iterator
@@ -24,8 +24,8 @@ from google.protobuf.message import Message as GrpcMessage
24
24
  from flwr.supercore.license_plugin import LicensePlugin
25
25
 
26
26
 
27
- class ExecLicenseInterceptor(grpc.ServerInterceptor): # type: ignore
28
- """Exec API interceptor for license checking."""
27
+ class ControlLicenseInterceptor(grpc.ServerInterceptor): # type: ignore
28
+ """Control API interceptor for license checking."""
29
29
 
30
30
  def __init__(self, license_plugin: LicensePlugin) -> None:
31
31
  """Initialize the interceptor with a license plugin."""
@@ -42,12 +42,12 @@ class ExecLicenseInterceptor(grpc.ServerInterceptor): # type: ignore
42
42
  Continue RPC call if license check is enabled and passes, else, terminate RPC
43
43
  call by setting context to abort.
44
44
  """
45
- # Only apply to Exec service
46
- if not handler_call_details.method.startswith("/flwr.proto.Exec/"):
45
+ # Only apply to Control service
46
+ if not handler_call_details.method.startswith("/flwr.proto.Control/"):
47
47
  return continuation(handler_call_details)
48
48
 
49
49
  # One of the method handlers in
50
- # `flwr.superexec.exec_servicer.ExecServicer`
50
+ # `flwr.superlink.servicer.control.ControlServicer`
51
51
  method_handler: grpc.RpcMethodHandler = continuation(handler_call_details)
52
52
  return self._generic_license_unary_method_handler(method_handler)
53
53