flwr-nightly 1.21.0.dev20250818__py3-none-any.whl → 1.21.0.dev20250820__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/simulation/app.py CHANGED
@@ -16,7 +16,6 @@
16
16
 
17
17
 
18
18
  import argparse
19
- import gc
20
19
  from logging import DEBUG, ERROR, INFO
21
20
  from queue import Queue
22
21
  from typing import Optional
@@ -71,7 +70,7 @@ from flwr.proto.simulationio_pb2_grpc import SimulationIoStub
71
70
  from flwr.server.superlink.fleet.vce.backend.backend import BackendConfig
72
71
  from flwr.simulation.run_simulation import _run_simulation
73
72
  from flwr.simulation.simulationio_connection import SimulationIoConnection
74
- from flwr.supercore.app_utils import simple_get_token, start_parent_process_monitor
73
+ from flwr.supercore.app_utils import start_parent_process_monitor
75
74
  from flwr.supercore.superexec.plugin import SimulationExecPlugin
76
75
  from flwr.supercore.superexec.run_superexec import run_with_deprecation_warning
77
76
 
@@ -114,7 +113,6 @@ def flwr_simulation() -> None:
114
113
  run_simulation_process(
115
114
  simulationio_api_address=args.simulationio_api_address,
116
115
  log_queue=log_queue,
117
- run_once=(args.token is not None) or args.run_once,
118
116
  token=args.token,
119
117
  flwr_dir_=args.flwr_dir,
120
118
  certificates=None,
@@ -128,8 +126,7 @@ def flwr_simulation() -> None:
128
126
  def run_simulation_process( # pylint: disable=R0913, R0914, R0915, R0917, W0212
129
127
  simulationio_api_address: str,
130
128
  log_queue: Queue[Optional[str]],
131
- run_once: bool,
132
- token: Optional[str] = None,
129
+ token: str,
133
130
  flwr_dir_: Optional[str] = None,
134
131
  certificates: Optional[bytes] = None,
135
132
  parent_pid: Optional[int] = None,
@@ -150,168 +147,144 @@ def run_simulation_process( # pylint: disable=R0913, R0914, R0915, R0917, W0212
150
147
  heartbeat_sender = None
151
148
  run_status = None
152
149
 
153
- while True:
150
+ try:
151
+ # Pull SimulationInputs from LinkState
152
+ req = PullAppInputsRequest(token=token)
153
+ res: PullAppInputsResponse = conn._stub.PullAppInputs(req)
154
+ context = context_from_proto(res.context)
155
+ run = run_from_proto(res.run)
156
+ fab = fab_from_proto(res.fab)
157
+
158
+ # Start log uploader for this run
159
+ log_uploader = start_log_uploader(
160
+ log_queue=log_queue,
161
+ node_id=context.node_id,
162
+ run_id=run.run_id,
163
+ stub=conn._stub,
164
+ )
154
165
 
155
- try:
156
- # If token is not set, loop until token is received from SuperNode
157
- if token is None:
158
- log(DEBUG, "[flwr-simulation] Request token")
159
- token = simple_get_token(conn._stub)
160
-
161
- # Pull SimulationInputs from LinkState
162
- req = PullAppInputsRequest(token=token)
163
- res: PullAppInputsResponse = conn._stub.PullAppInputs(req)
164
- context = context_from_proto(res.context)
165
- run = run_from_proto(res.run)
166
- fab = fab_from_proto(res.fab)
167
-
168
- # Start log uploader for this run
169
- log_uploader = start_log_uploader(
170
- log_queue=log_queue,
171
- node_id=context.node_id,
172
- run_id=run.run_id,
173
- stub=conn._stub,
174
- )
166
+ log(DEBUG, "Simulation process starts FAB installation.")
167
+ install_from_fab(fab.content, flwr_dir=flwr_dir, skip_prompt=True)
175
168
 
176
- log(DEBUG, "Simulation process starts FAB installation.")
177
- install_from_fab(fab.content, flwr_dir=flwr_dir, skip_prompt=True)
169
+ fab_id, fab_version = get_fab_metadata(fab.content)
178
170
 
179
- fab_id, fab_version = get_fab_metadata(fab.content)
171
+ app_path = get_project_dir(fab_id, fab_version, fab.hash_str, flwr_dir)
172
+ config = get_project_config(app_path)
180
173
 
181
- app_path = get_project_dir(fab_id, fab_version, fab.hash_str, flwr_dir)
182
- config = get_project_config(app_path)
174
+ # Get ClientApp and SeverApp components
175
+ app_components = config["tool"]["flwr"]["app"]["components"]
176
+ client_app_attr = app_components["clientapp"]
177
+ server_app_attr = app_components["serverapp"]
178
+ fused_config = get_fused_config_from_dir(app_path, run.override_config)
183
179
 
184
- # Get ClientApp and SeverApp components
185
- app_components = config["tool"]["flwr"]["app"]["components"]
186
- client_app_attr = app_components["clientapp"]
187
- server_app_attr = app_components["serverapp"]
188
- fused_config = get_fused_config_from_dir(app_path, run.override_config)
180
+ # Update run_config in context
181
+ context.run_config = fused_config
189
182
 
190
- # Update run_config in context
191
- context.run_config = fused_config
183
+ log(
184
+ DEBUG,
185
+ "Flower will load ServerApp `%s` in %s",
186
+ server_app_attr,
187
+ app_path,
188
+ )
189
+ log(
190
+ DEBUG,
191
+ "Flower will load ClientApp `%s` in %s",
192
+ client_app_attr,
193
+ app_path,
194
+ )
192
195
 
193
- log(
194
- DEBUG,
195
- "Flower will load ServerApp `%s` in %s",
196
- server_app_attr,
197
- app_path,
198
- )
199
- log(
200
- DEBUG,
201
- "Flower will load ClientApp `%s` in %s",
202
- client_app_attr,
203
- app_path,
204
- )
196
+ # Change status to Running
197
+ run_status_proto = run_status_to_proto(RunStatus(Status.RUNNING, "", ""))
198
+ conn._stub.UpdateRunStatus(
199
+ UpdateRunStatusRequest(run_id=run.run_id, run_status=run_status_proto)
200
+ )
205
201
 
206
- # Change status to Running
207
- run_status_proto = run_status_to_proto(RunStatus(Status.RUNNING, "", ""))
208
- conn._stub.UpdateRunStatus(
209
- UpdateRunStatusRequest(run_id=run.run_id, run_status=run_status_proto)
210
- )
202
+ # Pull Federation Options
203
+ fed_opt_res: GetFederationOptionsResponse = conn._stub.GetFederationOptions(
204
+ GetFederationOptionsRequest(run_id=run.run_id)
205
+ )
206
+ federation_options = config_record_from_proto(fed_opt_res.federation_options)
207
+
208
+ # Unflatten underlying dict
209
+ fed_opt = unflatten_dict({**federation_options})
210
+
211
+ # Extract configs values of interest
212
+ num_supernodes = fed_opt.get("num-supernodes")
213
+ if num_supernodes is None:
214
+ raise ValueError("Federation options expects `num-supernodes` to be set.")
215
+ backend_config: BackendConfig = fed_opt.get("backend", {})
216
+ verbose: bool = fed_opt.get("verbose", False)
217
+ enable_tf_gpu_growth: bool = fed_opt.get("enable_tf_gpu_growth", False)
218
+
219
+ event(
220
+ EventType.FLWR_SIMULATION_RUN_ENTER,
221
+ event_details={
222
+ "backend": "ray",
223
+ "num-supernodes": num_supernodes,
224
+ "run-id-hash": get_sha256_hash(run.run_id),
225
+ },
226
+ )
211
227
 
212
- # Pull Federation Options
213
- fed_opt_res: GetFederationOptionsResponse = conn._stub.GetFederationOptions(
214
- GetFederationOptionsRequest(run_id=run.run_id)
215
- )
216
- federation_options = config_record_from_proto(
217
- fed_opt_res.federation_options
218
- )
228
+ # Set up heartbeat sender
229
+ heartbeat_fn = get_grpc_app_heartbeat_fn(
230
+ conn._stub,
231
+ run.run_id,
232
+ failure_message="Heartbeat failed unexpectedly. The SuperLink could "
233
+ "not find the provided run ID, or the run status is invalid.",
234
+ )
235
+ heartbeat_sender = HeartbeatSender(heartbeat_fn)
236
+ heartbeat_sender.start()
237
+
238
+ # Launch the simulation
239
+ updated_context = _run_simulation(
240
+ server_app_attr=server_app_attr,
241
+ client_app_attr=client_app_attr,
242
+ num_supernodes=num_supernodes,
243
+ backend_config=backend_config,
244
+ app_dir=str(app_path),
245
+ run=run,
246
+ enable_tf_gpu_growth=enable_tf_gpu_growth,
247
+ verbose_logging=verbose,
248
+ server_app_run_config=fused_config,
249
+ is_app=True,
250
+ exit_event=EventType.FLWR_SIMULATION_RUN_LEAVE,
251
+ )
219
252
 
220
- # Unflatten underlying dict
221
- fed_opt = unflatten_dict({**federation_options})
222
-
223
- # Extract configs values of interest
224
- num_supernodes = fed_opt.get("num-supernodes")
225
- if num_supernodes is None:
226
- raise ValueError(
227
- "Federation options expects `num-supernodes` to be set."
228
- )
229
- backend_config: BackendConfig = fed_opt.get("backend", {})
230
- verbose: bool = fed_opt.get("verbose", False)
231
- enable_tf_gpu_growth: bool = fed_opt.get("enable_tf_gpu_growth", False)
232
-
233
- event(
234
- EventType.FLWR_SIMULATION_RUN_ENTER,
235
- event_details={
236
- "backend": "ray",
237
- "num-supernodes": num_supernodes,
238
- "run-id-hash": get_sha256_hash(run.run_id),
239
- },
240
- )
253
+ # Send resulting context
254
+ context_proto = context_to_proto(updated_context)
255
+ out_req = PushAppOutputsRequest(
256
+ token=token, run_id=run.run_id, context=context_proto
257
+ )
258
+ _ = conn._stub.PushAppOutputs(out_req)
241
259
 
242
- # Set up heartbeat sender
243
- heartbeat_fn = get_grpc_app_heartbeat_fn(
244
- conn._stub,
245
- run.run_id,
246
- failure_message="Heartbeat failed unexpectedly. The SuperLink could "
247
- "not find the provided run ID, or the run status is invalid.",
248
- )
249
- heartbeat_sender = HeartbeatSender(heartbeat_fn)
250
- heartbeat_sender.start()
251
-
252
- # Launch the simulation
253
- updated_context = _run_simulation(
254
- server_app_attr=server_app_attr,
255
- client_app_attr=client_app_attr,
256
- num_supernodes=num_supernodes,
257
- backend_config=backend_config,
258
- app_dir=str(app_path),
259
- run=run,
260
- enable_tf_gpu_growth=enable_tf_gpu_growth,
261
- verbose_logging=verbose,
262
- server_app_run_config=fused_config,
263
- is_app=True,
264
- exit_event=EventType.FLWR_SIMULATION_RUN_LEAVE,
265
- )
260
+ run_status = RunStatus(Status.FINISHED, SubStatus.COMPLETED, "")
266
261
 
267
- # Send resulting context
268
- context_proto = context_to_proto(updated_context)
269
- out_req = PushAppOutputsRequest(
270
- token=token, run_id=run.run_id, context=context_proto
262
+ except Exception as ex: # pylint: disable=broad-exception-caught
263
+ exc_entity = "Simulation"
264
+ log(ERROR, "%s raised an exception", exc_entity, exc_info=ex)
265
+ run_status = RunStatus(Status.FINISHED, SubStatus.FAILED, str(ex))
266
+
267
+ finally:
268
+ # Stop heartbeat sender
269
+ if heartbeat_sender:
270
+ heartbeat_sender.stop()
271
+
272
+ # Stop log uploader for this run and upload final logs
273
+ if log_uploader:
274
+ stop_log_uploader(log_queue, log_uploader)
275
+
276
+ # Update run status
277
+ if run_status:
278
+ run_status_proto = run_status_to_proto(run_status)
279
+ conn._stub.UpdateRunStatus(
280
+ UpdateRunStatusRequest(run_id=run.run_id, run_status=run_status_proto)
271
281
  )
272
- _ = conn._stub.PushAppOutputs(out_req)
273
-
274
- run_status = RunStatus(Status.FINISHED, SubStatus.COMPLETED, "")
275
-
276
- except Exception as ex: # pylint: disable=broad-exception-caught
277
- exc_entity = "Simulation"
278
- log(ERROR, "%s raised an exception", exc_entity, exc_info=ex)
279
- run_status = RunStatus(Status.FINISHED, SubStatus.FAILED, str(ex))
280
-
281
- finally:
282
- # Stop heartbeat sender
283
- if heartbeat_sender:
284
- heartbeat_sender.stop()
285
- heartbeat_sender = None
286
-
287
- # Stop log uploader for this run and upload final logs
288
- if log_uploader:
289
- stop_log_uploader(log_queue, log_uploader)
290
- log_uploader = None
291
-
292
- # Update run status
293
- if run_status:
294
- run_status_proto = run_status_to_proto(run_status)
295
- conn._stub.UpdateRunStatus(
296
- UpdateRunStatusRequest(
297
- run_id=run.run_id, run_status=run_status_proto
298
- )
299
- )
300
- run_status = None
301
-
302
- # Clean up the Context if it exists
303
- try:
304
- del updated_context
305
- except NameError:
306
- pass
307
-
308
- # Remove the token
309
- token = None
310
- gc.collect()
311
-
312
- # Stop the loop if `flwr-simulation` is expected to process a single run
313
- if run_once:
314
- break
282
+
283
+ # Clean up the Context if it exists
284
+ try:
285
+ del updated_context
286
+ except NameError:
287
+ pass
315
288
 
316
289
 
317
290
  def _parse_args_run_flwr_simulation() -> argparse.ArgumentParser:
@@ -19,17 +19,6 @@ import os
19
19
  import signal
20
20
  import threading
21
21
  import time
22
- from typing import Union
23
-
24
- from flwr.proto.appio_pb2 import ( # pylint: disable=E0611
25
- ListAppsToLaunchRequest,
26
- ListAppsToLaunchResponse,
27
- RequestTokenRequest,
28
- RequestTokenResponse,
29
- )
30
- from flwr.proto.clientappio_pb2_grpc import ClientAppIoStub
31
- from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub
32
- from flwr.proto.simulationio_pb2_grpc import SimulationIoStub
33
22
 
34
23
  if os.name == "nt":
35
24
  from ctypes import windll # type: ignore
@@ -67,23 +56,3 @@ def start_parent_process_monitor(
67
56
  os.kill(os.getpid(), signal.SIGKILL)
68
57
 
69
58
  threading.Thread(target=monitor, daemon=True).start()
70
-
71
-
72
- def simple_get_token(
73
- stub: Union[ClientAppIoStub, ServerAppIoStub, SimulationIoStub]
74
- ) -> str:
75
- """Get a token from SuperLink/SuperNode.
76
-
77
- This shall be removed once the SuperExec is fully implemented.
78
- """
79
- while True:
80
- res: ListAppsToLaunchResponse = stub.ListAppsToLaunch(ListAppsToLaunchRequest())
81
-
82
- for run_id in res.run_ids:
83
- tk_res: RequestTokenResponse = stub.RequestToken(
84
- RequestTokenRequest(run_id=run_id)
85
- )
86
- if tk_res.token:
87
- return tk_res.token
88
-
89
- time.sleep(1) # Wait before retrying to get run IDs
@@ -20,6 +20,7 @@ from logging import INFO
20
20
 
21
21
  from flwr.common import EventType, event
22
22
  from flwr.common.constant import ExecPluginType
23
+ from flwr.common.exit import ExitCode, flwr_exit
23
24
  from flwr.common.logger import log
24
25
  from flwr.proto.clientappio_pb2_grpc import ClientAppIoStub
25
26
  from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub
@@ -36,6 +37,11 @@ from flwr.supercore.superexec.run_superexec import run_superexec
36
37
  def flower_superexec() -> None:
37
38
  """Run `flower-superexec` command."""
38
39
  args = _parse_args().parse_args()
40
+ if not args.insecure:
41
+ flwr_exit(
42
+ ExitCode.COMMON_TLS_NOT_SUPPORTED,
43
+ "SuperExec does not support TLS yet.",
44
+ )
39
45
 
40
46
  # Log the first message after parsing arguments in case of `--help`
41
47
  log(INFO, "Starting Flower SuperExec")
@@ -26,14 +26,12 @@ 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
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
34
 
36
- from ...executor import Executor
37
35
  from .control_event_log_interceptor import ControlEventLogInterceptor
38
36
  from .control_license_interceptor import ControlLicenseInterceptor
39
37
  from .control_servicer import ControlServicer
@@ -50,19 +48,16 @@ except ImportError:
50
48
  # pylint: disable-next=too-many-arguments,too-many-positional-arguments,too-many-locals
51
49
  def run_control_api_grpc(
52
50
  address: str,
53
- executor: Executor,
54
51
  state_factory: LinkStateFactory,
55
52
  ffs_factory: FfsFactory,
56
53
  objectstore_factory: ObjectStoreFactory,
57
54
  certificates: Optional[tuple[bytes, bytes, bytes]],
58
- config: UserConfig,
55
+ is_simulation: bool,
59
56
  auth_plugin: Optional[ControlAuthPlugin] = None,
60
57
  authz_plugin: Optional[ControlAuthzPlugin] = None,
61
58
  event_log_plugin: Optional[EventLogWriterPlugin] = None,
62
59
  ) -> grpc.Server:
63
60
  """Run Control API (gRPC, request-response)."""
64
- executor.set_config(config)
65
-
66
61
  license_plugin: Optional[LicensePlugin] = get_license_plugin()
67
62
  if license_plugin and not license_plugin.check_license():
68
63
  flwr_exit(ExitCode.SUPERLINK_LICENSE_INVALID)
@@ -71,7 +66,7 @@ def run_control_api_grpc(
71
66
  linkstate_factory=state_factory,
72
67
  ffs_factory=ffs_factory,
73
68
  objectstore_factory=objectstore_factory,
74
- executor=executor,
69
+ is_simulation=is_simulation,
75
70
  auth_plugin=auth_plugin,
76
71
  )
77
72
  interceptors: list[grpc.ServerInterceptor] = []
@@ -93,11 +88,11 @@ def run_control_api_grpc(
93
88
  )
94
89
 
95
90
  if auth_plugin is None:
96
- log(INFO, "Flower Deployment Engine: Starting Control API on %s", address)
91
+ log(INFO, "Flower Deployment Runtime: Starting Control API on %s", address)
97
92
  else:
98
93
  log(
99
94
  INFO,
100
- "Flower Deployment Engine: Starting Control API with user "
95
+ "Flower Deployment Runtime: Starting Control API with user "
101
96
  "authentication on %s",
102
97
  address,
103
98
  )
@@ -15,6 +15,7 @@
15
15
  """Control API servicer."""
16
16
 
17
17
 
18
+ import hashlib
18
19
  import time
19
20
  from collections.abc import Generator
20
21
  from logging import ERROR, INFO
@@ -22,7 +23,8 @@ from typing import Any, Optional, cast
22
23
 
23
24
  import grpc
24
25
 
25
- from flwr.common import now
26
+ from flwr.cli.config_utils import get_fab_metadata
27
+ from flwr.common import Context, RecordDict, now
26
28
  from flwr.common.auth_plugin import ControlAuthPlugin
27
29
  from flwr.common.constant import (
28
30
  FAB_MAX_SIZE,
@@ -37,7 +39,7 @@ from flwr.common.serde import (
37
39
  run_to_proto,
38
40
  user_config_from_proto,
39
41
  )
40
- from flwr.common.typing import Run, RunStatus
42
+ from flwr.common.typing import Fab, Run, RunStatus
41
43
  from flwr.proto import control_pb2_grpc # pylint: disable=E0611
42
44
  from flwr.proto.control_pb2 import ( # pylint: disable=E0611
43
45
  GetAuthTokensRequest,
@@ -57,7 +59,6 @@ from flwr.server.superlink.linkstate import LinkState, LinkStateFactory
57
59
  from flwr.supercore.ffs import FfsFactory
58
60
  from flwr.supercore.object_store import ObjectStore, ObjectStoreFactory
59
61
 
60
- from ...executor.executor import Executor
61
62
  from .control_user_auth_interceptor import shared_account_info
62
63
 
63
64
 
@@ -69,14 +70,13 @@ class ControlServicer(control_pb2_grpc.ControlServicer):
69
70
  linkstate_factory: LinkStateFactory,
70
71
  ffs_factory: FfsFactory,
71
72
  objectstore_factory: ObjectStoreFactory,
72
- executor: Executor,
73
+ is_simulation: bool,
73
74
  auth_plugin: Optional[ControlAuthPlugin] = None,
74
75
  ) -> None:
75
76
  self.linkstate_factory = linkstate_factory
76
77
  self.ffs_factory = ffs_factory
77
78
  self.objectstore_factory = objectstore_factory
78
- self.executor = executor
79
- self.executor.initialize(linkstate_factory, ffs_factory)
79
+ self.is_simulation = is_simulation
80
80
  self.auth_plugin = auth_plugin
81
81
 
82
82
  def StartRun(
@@ -84,6 +84,8 @@ class ControlServicer(control_pb2_grpc.ControlServicer):
84
84
  ) -> StartRunResponse:
85
85
  """Create run ID."""
86
86
  log(INFO, "ControlServicer.StartRun")
87
+ state = self.linkstate_factory.state()
88
+ ffs = self.ffs_factory.ffs()
87
89
 
88
90
  if len(request.fab.content) > FAB_MAX_SIZE:
89
91
  log(
@@ -94,17 +96,53 @@ class ControlServicer(control_pb2_grpc.ControlServicer):
94
96
  return StartRunResponse()
95
97
 
96
98
  flwr_aid = shared_account_info.get().flwr_aid if self.auth_plugin else None
97
- run_id = self.executor.start_run(
98
- request.fab.content,
99
- user_config_from_proto(request.override_config),
100
- config_record_from_proto(request.federation_options),
101
- flwr_aid,
102
- )
99
+ override_config = user_config_from_proto(request.override_config)
100
+ federation_options = config_record_from_proto(request.federation_options)
101
+ fab_file = request.fab.content
102
+
103
+ try:
104
+ # Check that num-supernodes is set
105
+ if self.is_simulation and "num-supernodes" not in federation_options:
106
+ raise ValueError(
107
+ "Federation options doesn't contain key `num-supernodes`."
108
+ )
109
+
110
+ # Create run
111
+ fab = Fab(hashlib.sha256(fab_file).hexdigest(), fab_file)
112
+ fab_hash = ffs.put(fab.content, {})
113
+ if fab_hash != fab.hash_str:
114
+ raise RuntimeError(
115
+ f"FAB ({fab.hash_str}) hash from request doesn't match contents"
116
+ )
117
+ fab_id, fab_version = get_fab_metadata(fab.content)
118
+
119
+ run_id = state.create_run(
120
+ fab_id,
121
+ fab_version,
122
+ fab_hash,
123
+ override_config,
124
+ federation_options,
125
+ flwr_aid,
126
+ )
127
+
128
+ # Create an empty context for the Run
129
+ context = Context(
130
+ run_id=run_id,
131
+ node_id=0,
132
+ node_config={},
133
+ state=RecordDict(),
134
+ run_config={},
135
+ )
136
+
137
+ # Register the context at the LinkState
138
+ state.set_serverapp_context(run_id=run_id, context=context)
103
139
 
104
- if run_id is None:
105
- log(ERROR, "Executor failed to start run")
140
+ # pylint: disable-next=broad-except
141
+ except Exception as e:
142
+ log(ERROR, "Could not start run: %s", str(e))
106
143
  return StartRunResponse()
107
144
 
145
+ log(INFO, "Created run %s", str(run_id))
108
146
  return StartRunResponse(run_id=run_id)
109
147
 
110
148
  def StreamLogs( # pylint: disable=C0103
@@ -532,6 +532,6 @@ def run_clientappio_api_grpc(
532
532
  max_message_length=GRPC_MAX_MESSAGE_LENGTH,
533
533
  certificates=certificates,
534
534
  )
535
- log(INFO, "Starting Flower ClientAppIo gRPC server on %s", address)
535
+ log(INFO, "Flower Deployment Runtime: Starting ClientAppIo API on %s", address)
536
536
  clientappio_grpc_server.start()
537
537
  return clientappio_grpc_server
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: flwr-nightly
3
- Version: 1.21.0.dev20250818
3
+ Version: 1.21.0.dev20250820
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  License: Apache-2.0
6
6
  Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning