flwr 1.13.0__py3-none-any.whl → 1.14.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 (120) hide show
  1. flwr/cli/app.py +5 -0
  2. flwr/cli/build.py +1 -37
  3. flwr/cli/cli_user_auth_interceptor.py +86 -0
  4. flwr/cli/config_utils.py +19 -2
  5. flwr/cli/example.py +1 -0
  6. flwr/cli/install.py +2 -19
  7. flwr/cli/log.py +18 -36
  8. flwr/cli/login/__init__.py +22 -0
  9. flwr/cli/login/login.py +81 -0
  10. flwr/cli/ls.py +205 -106
  11. flwr/cli/new/__init__.py +1 -0
  12. flwr/cli/new/new.py +25 -14
  13. flwr/cli/new/templates/app/.gitignore.tpl +3 -0
  14. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
  15. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +3 -3
  16. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
  17. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
  18. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +2 -3
  19. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
  20. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
  21. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
  22. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
  23. flwr/cli/run/__init__.py +1 -0
  24. flwr/cli/run/run.py +89 -39
  25. flwr/cli/stop.py +130 -0
  26. flwr/cli/utils.py +172 -8
  27. flwr/client/app.py +14 -3
  28. flwr/client/client.py +1 -32
  29. flwr/client/clientapp/app.py +4 -8
  30. flwr/client/clientapp/utils.py +1 -0
  31. flwr/client/grpc_adapter_client/connection.py +1 -1
  32. flwr/client/grpc_client/connection.py +1 -1
  33. flwr/client/grpc_rere_client/connection.py +13 -7
  34. flwr/client/message_handler/message_handler.py +1 -2
  35. flwr/client/mod/comms_mods.py +1 -0
  36. flwr/client/mod/localdp_mod.py +1 -1
  37. flwr/client/nodestate/__init__.py +1 -0
  38. flwr/client/nodestate/nodestate.py +1 -0
  39. flwr/client/nodestate/nodestate_factory.py +1 -0
  40. flwr/client/numpy_client.py +0 -44
  41. flwr/client/rest_client/connection.py +3 -3
  42. flwr/client/supernode/app.py +2 -2
  43. flwr/common/address.py +1 -0
  44. flwr/common/args.py +1 -0
  45. flwr/common/auth_plugin/__init__.py +24 -0
  46. flwr/common/auth_plugin/auth_plugin.py +111 -0
  47. flwr/common/config.py +3 -1
  48. flwr/common/constant.py +17 -1
  49. flwr/common/logger.py +40 -0
  50. flwr/common/message.py +1 -0
  51. flwr/common/object_ref.py +57 -54
  52. flwr/common/pyproject.py +1 -0
  53. flwr/common/record/__init__.py +1 -0
  54. flwr/common/record/parametersrecord.py +1 -0
  55. flwr/common/retry_invoker.py +77 -0
  56. flwr/common/secure_aggregation/secaggplus_utils.py +2 -2
  57. flwr/common/telemetry.py +15 -4
  58. flwr/common/typing.py +12 -0
  59. flwr/common/version.py +1 -0
  60. flwr/proto/exec_pb2.py +38 -14
  61. flwr/proto/exec_pb2.pyi +107 -2
  62. flwr/proto/exec_pb2_grpc.py +102 -0
  63. flwr/proto/exec_pb2_grpc.pyi +39 -0
  64. flwr/proto/fab_pb2.py +4 -4
  65. flwr/proto/fab_pb2.pyi +4 -1
  66. flwr/proto/serverappio_pb2.py +18 -18
  67. flwr/proto/serverappio_pb2.pyi +8 -2
  68. flwr/proto/serverappio_pb2_grpc.py +34 -0
  69. flwr/proto/serverappio_pb2_grpc.pyi +13 -0
  70. flwr/proto/simulationio_pb2.py +2 -2
  71. flwr/proto/simulationio_pb2_grpc.py +34 -0
  72. flwr/proto/simulationio_pb2_grpc.pyi +13 -0
  73. flwr/server/app.py +62 -7
  74. flwr/server/compat/app_utils.py +7 -1
  75. flwr/server/driver/grpc_driver.py +11 -63
  76. flwr/server/driver/inmemory_driver.py +5 -1
  77. flwr/server/run_serverapp.py +8 -9
  78. flwr/server/serverapp/app.py +25 -10
  79. flwr/server/strategy/dpfedavg_fixed.py +1 -0
  80. flwr/server/superlink/driver/serverappio_grpc.py +1 -0
  81. flwr/server/superlink/driver/serverappio_servicer.py +82 -23
  82. flwr/server/superlink/ffs/disk_ffs.py +1 -0
  83. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +1 -0
  84. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -0
  85. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +32 -12
  86. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +12 -11
  87. flwr/server/superlink/fleet/message_handler/message_handler.py +32 -5
  88. flwr/server/superlink/fleet/rest_rere/rest_api.py +4 -1
  89. flwr/server/superlink/fleet/vce/__init__.py +1 -0
  90. flwr/server/superlink/fleet/vce/backend/__init__.py +1 -0
  91. flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -0
  92. flwr/server/superlink/linkstate/in_memory_linkstate.py +21 -30
  93. flwr/server/superlink/linkstate/linkstate.py +17 -2
  94. flwr/server/superlink/linkstate/sqlite_linkstate.py +30 -49
  95. flwr/server/superlink/simulation/simulationio_servicer.py +33 -0
  96. flwr/server/superlink/utils.py +65 -0
  97. flwr/simulation/app.py +59 -52
  98. flwr/simulation/ray_transport/ray_actor.py +1 -0
  99. flwr/simulation/ray_transport/utils.py +1 -0
  100. flwr/simulation/run_simulation.py +36 -22
  101. flwr/simulation/simulationio_connection.py +3 -0
  102. flwr/superexec/app.py +1 -0
  103. flwr/superexec/deployment.py +1 -0
  104. flwr/superexec/exec_grpc.py +19 -1
  105. flwr/superexec/exec_servicer.py +76 -2
  106. flwr/superexec/exec_user_auth_interceptor.py +101 -0
  107. flwr/superexec/executor.py +1 -0
  108. {flwr-1.13.0.dist-info → flwr-1.14.0.dist-info}/METADATA +8 -8
  109. {flwr-1.13.0.dist-info → flwr-1.14.0.dist-info}/RECORD +112 -112
  110. flwr/proto/common_pb2.py +0 -36
  111. flwr/proto/common_pb2.pyi +0 -121
  112. flwr/proto/common_pb2_grpc.py +0 -4
  113. flwr/proto/common_pb2_grpc.pyi +0 -4
  114. flwr/proto/control_pb2.py +0 -27
  115. flwr/proto/control_pb2.pyi +0 -7
  116. flwr/proto/control_pb2_grpc.py +0 -135
  117. flwr/proto/control_pb2_grpc.pyi +0 -53
  118. {flwr-1.13.0.dist-info → flwr-1.14.0.dist-info}/LICENSE +0 -0
  119. {flwr-1.13.0.dist-info → flwr-1.14.0.dist-info}/WHEEL +0 -0
  120. {flwr-1.13.0.dist-info → flwr-1.14.0.dist-info}/entry_points.txt +0 -0
flwr/cli/ls.py CHANGED
@@ -15,27 +15,26 @@
15
15
  """Flower command line interface `ls` command."""
16
16
 
17
17
 
18
+ import io
19
+ import json
18
20
  from datetime import datetime, timedelta
19
- from logging import DEBUG
20
21
  from pathlib import Path
21
- from typing import Annotated, Any, Optional
22
+ from typing import Annotated, Optional
22
23
 
23
- import grpc
24
24
  import typer
25
25
  from rich.console import Console
26
26
  from rich.table import Table
27
27
  from rich.text import Text
28
28
 
29
29
  from flwr.cli.config_utils import (
30
+ exit_if_no_address,
30
31
  load_and_validate,
31
- validate_certificate_in_federation_config,
32
+ process_loaded_project_config,
32
33
  validate_federation_in_project_config,
33
- validate_project_config,
34
34
  )
35
- from flwr.common.constant import FAB_CONFIG_FILE, SubStatus
35
+ from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat, SubStatus
36
36
  from flwr.common.date import format_timedelta, isoformat8601_utc
37
- from flwr.common.grpc import GRPC_MAX_MESSAGE_LENGTH, create_channel
38
- from flwr.common.logger import log
37
+ from flwr.common.logger import print_json_error, redirect_output, restore_output
39
38
  from flwr.common.serde import run_from_proto
40
39
  from flwr.common.typing import Run
41
40
  from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
@@ -44,8 +43,12 @@ from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
44
43
  )
45
44
  from flwr.proto.exec_pb2_grpc import ExecStub
46
45
 
46
+ from .utils import init_channel, try_obtain_cli_auth_plugin, unauthenticated_exc_handler
47
47
 
48
- def ls(
48
+ _RunListType = tuple[int, str, str, str, str, str, str, str, str]
49
+
50
+
51
+ def ls( # pylint: disable=too-many-locals, too-many-branches
49
52
  app: Annotated[
50
53
  Path,
51
54
  typer.Argument(help="Path of the Flower project"),
@@ -68,94 +71,100 @@ def ls(
68
71
  help="Specific run ID to display",
69
72
  ),
70
73
  ] = None,
74
+ output_format: Annotated[
75
+ str,
76
+ typer.Option(
77
+ "--format",
78
+ case_sensitive=False,
79
+ help="Format output using 'default' view or 'json'",
80
+ ),
81
+ ] = CliOutputFormat.DEFAULT,
71
82
  ) -> None:
72
- """List runs."""
73
- # Load and validate federation config
74
- typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
75
-
76
- pyproject_path = app / FAB_CONFIG_FILE if app else None
77
- config, errors, warnings = load_and_validate(path=pyproject_path)
78
- config = validate_project_config(config, errors, warnings)
79
- federation, federation_config = validate_federation_in_project_config(
80
- federation, config
81
- )
82
-
83
- if "address" not in federation_config:
84
- typer.secho(
85
- "❌ `flwr log` currently works with Exec API. Ensure that the correct"
86
- "Exec API address is provided in the `pyproject.toml`.",
87
- fg=typer.colors.RED,
88
- bold=True,
89
- )
90
- raise typer.Exit(code=1)
91
-
83
+ """List the details of one provided run ID or all runs in a Flower federation.
84
+
85
+ The following details are displayed:
86
+
87
+ - **Run ID:** Unique identifier for the run.
88
+ - **FAB:** Name of the FAB associated with the run (``{FAB_ID} (v{FAB_VERSION})``).
89
+ - **Status:** Current status of the run (pending, starting, running, finished).
90
+ - **Elapsed:** Time elapsed since the run started (``HH:MM:SS``).
91
+ - **Created At:** Timestamp when the run was created.
92
+ - **Running At:** Timestamp when the run started running.
93
+ - **Finished At:** Timestamp when the run finished.
94
+
95
+ All timestamps follow ISO 8601, UTC and are formatted as ``YYYY-MM-DD HH:MM:SSZ``.
96
+ """
97
+ suppress_output = output_format == CliOutputFormat.JSON
98
+ captured_output = io.StringIO()
92
99
  try:
93
- if runs and run_id is not None:
94
- raise ValueError(
95
- "The options '--runs' and '--run-id' are mutually exclusive."
100
+ if suppress_output:
101
+ redirect_output(captured_output)
102
+ # Load and validate federation config
103
+ typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
104
+
105
+ pyproject_path = app / FAB_CONFIG_FILE if app else None
106
+ config, errors, warnings = load_and_validate(path=pyproject_path)
107
+ config = process_loaded_project_config(config, errors, warnings)
108
+ federation, federation_config = validate_federation_in_project_config(
109
+ federation, config
110
+ )
111
+ exit_if_no_address(federation_config, "ls")
112
+
113
+ try:
114
+ if runs and run_id is not None:
115
+ raise ValueError(
116
+ "The options '--runs' and '--run-id' are mutually exclusive."
117
+ )
118
+ auth_plugin = try_obtain_cli_auth_plugin(app, federation)
119
+ channel = init_channel(app, federation_config, auth_plugin)
120
+ stub = ExecStub(channel)
121
+
122
+ # Display information about a specific run ID
123
+ if run_id is not None:
124
+ typer.echo(f"🔍 Displaying information for run ID {run_id}...")
125
+ restore_output()
126
+ _display_one_run(stub, run_id, output_format)
127
+ # By default, list all runs
128
+ else:
129
+ typer.echo("📄 Listing all runs...")
130
+ restore_output()
131
+ _list_runs(stub, output_format)
132
+
133
+ except ValueError as err:
134
+ if suppress_output:
135
+ redirect_output(captured_output)
136
+ typer.secho(
137
+ f"❌ {err}",
138
+ fg=typer.colors.RED,
139
+ bold=True,
96
140
  )
97
-
98
- channel = _init_channel(app, federation_config)
99
- stub = ExecStub(channel)
100
-
101
- # Display information about a specific run ID
102
- if run_id is not None:
103
- typer.echo(f"🔍 Displaying information for run ID {run_id}...")
104
- _display_one_run(stub, run_id)
105
- # By default, list all runs
141
+ raise typer.Exit(code=1) from err
142
+ finally:
143
+ channel.close()
144
+ except (typer.Exit, Exception) as err: # pylint: disable=broad-except
145
+ if suppress_output:
146
+ restore_output()
147
+ e_message = captured_output.getvalue()
148
+ print_json_error(e_message, err)
106
149
  else:
107
- typer.echo("📄 Listing all runs...")
108
- _list_runs(stub)
109
-
110
- except ValueError as err:
111
- typer.secho(
112
- f"❌ {err}",
113
- fg=typer.colors.RED,
114
- bold=True,
115
- )
116
- raise typer.Exit(code=1) from err
150
+ typer.secho(
151
+ f"{err}",
152
+ fg=typer.colors.RED,
153
+ bold=True,
154
+ )
117
155
  finally:
118
- channel.close()
156
+ if suppress_output:
157
+ restore_output()
158
+ captured_output.close()
119
159
 
120
160
 
121
- def on_channel_state_change(channel_connectivity: str) -> None:
122
- """Log channel connectivity."""
123
- log(DEBUG, channel_connectivity)
124
-
125
-
126
- def _init_channel(app: Path, federation_config: dict[str, Any]) -> grpc.Channel:
127
- """Initialize gRPC channel to the Exec API."""
128
- insecure, root_certificates_bytes = validate_certificate_in_federation_config(
129
- app, federation_config
130
- )
131
- channel = create_channel(
132
- server_address=federation_config["address"],
133
- insecure=insecure,
134
- root_certificates=root_certificates_bytes,
135
- max_message_length=GRPC_MAX_MESSAGE_LENGTH,
136
- interceptors=None,
137
- )
138
- channel.subscribe(on_channel_state_change)
139
- return channel
140
-
141
-
142
- def _format_run_table(run_dict: dict[int, Run], now_isoformat: str) -> Table:
143
- """Format run status as a rich Table."""
144
- table = Table(header_style="bold cyan", show_lines=True)
161
+ def _format_runs(run_dict: dict[int, Run], now_isoformat: str) -> list[_RunListType]:
162
+ """Format runs to a list."""
145
163
 
146
164
  def _format_datetime(dt: Optional[datetime]) -> str:
147
165
  return isoformat8601_utc(dt).replace("T", " ") if dt else "N/A"
148
166
 
149
- # Add columns
150
- table.add_column(
151
- Text("Run ID", justify="center"), style="bright_white", overflow="fold"
152
- )
153
- table.add_column(Text("FAB", justify="center"), style="dim white")
154
- table.add_column(Text("Status", justify="center"))
155
- table.add_column(Text("Elapsed", justify="center"), style="blue")
156
- table.add_column(Text("Created At", justify="center"), style="dim white")
157
- table.add_column(Text("Running At", justify="center"), style="dim white")
158
- table.add_column(Text("Finished At", justify="center"), style="dim white")
167
+ run_list: list[_RunListType] = []
159
168
 
160
169
  # Add rows
161
170
  for run in sorted(
@@ -167,15 +176,6 @@ def _format_run_table(run_dict: dict[int, Run], now_isoformat: str) -> Table:
167
176
  else:
168
177
  status_text = f"{run.status.status}:{run.status.sub_status}"
169
178
 
170
- # Style the status based on its value
171
- sub_status = run.status.sub_status
172
- if sub_status == SubStatus.COMPLETED:
173
- status_style = "green"
174
- elif sub_status == SubStatus.FAILED:
175
- status_style = "red"
176
- else:
177
- status_style = "yellow"
178
-
179
179
  # Convert isoformat to datetime
180
180
  pending_at = datetime.fromisoformat(run.pending_at) if run.pending_at else None
181
181
  running_at = datetime.fromisoformat(run.running_at) if run.running_at else None
@@ -192,37 +192,136 @@ def _format_run_table(run_dict: dict[int, Run], now_isoformat: str) -> Table:
192
192
  end_time = datetime.fromisoformat(now_isoformat)
193
193
  elapsed_time = end_time - running_at
194
194
 
195
- table.add_row(
196
- f"[bold]{run.run_id}[/bold]",
197
- f"{run.fab_id} (v{run.fab_version})",
195
+ run_list.append(
196
+ (
197
+ run.run_id,
198
+ run.fab_id,
199
+ run.fab_version,
200
+ run.fab_hash,
201
+ status_text,
202
+ format_timedelta(elapsed_time),
203
+ _format_datetime(pending_at),
204
+ _format_datetime(running_at),
205
+ _format_datetime(finished_at),
206
+ )
207
+ )
208
+ return run_list
209
+
210
+
211
+ def _to_table(run_list: list[_RunListType]) -> Table:
212
+ """Format the provided run list to a rich Table."""
213
+ table = Table(header_style="bold cyan", show_lines=True)
214
+
215
+ # Add columns
216
+ table.add_column(
217
+ Text("Run ID", justify="center"), style="bright_white", overflow="fold"
218
+ )
219
+ table.add_column(Text("FAB", justify="center"), style="dim white")
220
+ table.add_column(Text("Status", justify="center"))
221
+ table.add_column(Text("Elapsed", justify="center"), style="blue")
222
+ table.add_column(Text("Created At", justify="center"), style="dim white")
223
+ table.add_column(Text("Running At", justify="center"), style="dim white")
224
+ table.add_column(Text("Finished At", justify="center"), style="dim white")
225
+
226
+ for row in run_list:
227
+ (
228
+ run_id,
229
+ fab_id,
230
+ fab_version,
231
+ _,
232
+ status_text,
233
+ elapsed,
234
+ created_at,
235
+ running_at,
236
+ finished_at,
237
+ ) = row
238
+ # Style the status based on its value
239
+ sub_status = status_text.rsplit(":", maxsplit=1)[-1]
240
+ if sub_status == SubStatus.COMPLETED:
241
+ status_style = "green"
242
+ elif sub_status == SubStatus.FAILED:
243
+ status_style = "red"
244
+ else:
245
+ status_style = "yellow"
246
+
247
+ formatted_row = (
248
+ f"[bold]{run_id}[/bold]",
249
+ f"{fab_id} (v{fab_version})",
198
250
  f"[{status_style}]{status_text}[/{status_style}]",
199
- format_timedelta(elapsed_time),
200
- _format_datetime(pending_at),
201
- _format_datetime(running_at),
202
- _format_datetime(finished_at),
251
+ elapsed,
252
+ created_at,
253
+ running_at,
254
+ finished_at,
203
255
  )
256
+ table.add_row(*formatted_row)
257
+
204
258
  return table
205
259
 
206
260
 
261
+ def _to_json(run_list: list[_RunListType]) -> str:
262
+ """Format run status list to a JSON formatted string."""
263
+ runs_list = []
264
+ for row in run_list:
265
+ (
266
+ run_id,
267
+ fab_id,
268
+ fab_version,
269
+ fab_hash,
270
+ status_text,
271
+ elapsed,
272
+ created_at,
273
+ running_at,
274
+ finished_at,
275
+ ) = row
276
+ runs_list.append(
277
+ {
278
+ "run-id": run_id,
279
+ "fab-id": fab_id,
280
+ "fab-name": fab_id.split("/")[-1],
281
+ "fab-version": fab_version,
282
+ "fab-hash": fab_hash[:8],
283
+ "status": status_text,
284
+ "elapsed": elapsed,
285
+ "created-at": created_at,
286
+ "running-at": running_at,
287
+ "finished-at": finished_at,
288
+ }
289
+ )
290
+
291
+ return json.dumps({"success": True, "runs": runs_list})
292
+
293
+
207
294
  def _list_runs(
208
295
  stub: ExecStub,
296
+ output_format: str = CliOutputFormat.DEFAULT,
209
297
  ) -> None:
210
298
  """List all runs."""
211
- res: ListRunsResponse = stub.ListRuns(ListRunsRequest())
299
+ with unauthenticated_exc_handler():
300
+ res: ListRunsResponse = stub.ListRuns(ListRunsRequest())
212
301
  run_dict = {run_id: run_from_proto(proto) for run_id, proto in res.run_dict.items()}
213
302
 
214
- Console().print(_format_run_table(run_dict, res.now))
303
+ formatted_runs = _format_runs(run_dict, res.now)
304
+ if output_format == CliOutputFormat.JSON:
305
+ Console().print_json(_to_json(formatted_runs))
306
+ else:
307
+ Console().print(_to_table(formatted_runs))
215
308
 
216
309
 
217
310
  def _display_one_run(
218
311
  stub: ExecStub,
219
312
  run_id: int,
313
+ output_format: str = CliOutputFormat.DEFAULT,
220
314
  ) -> None:
221
315
  """Display information about a specific run."""
222
- res: ListRunsResponse = stub.ListRuns(ListRunsRequest(run_id=run_id))
316
+ with unauthenticated_exc_handler():
317
+ res: ListRunsResponse = stub.ListRuns(ListRunsRequest(run_id=run_id))
223
318
  if not res.run_dict:
224
319
  raise ValueError(f"Run ID {run_id} not found")
225
320
 
226
321
  run_dict = {run_id: run_from_proto(proto) for run_id, proto in res.run_dict.items()}
227
322
 
228
- Console().print(_format_run_table(run_dict, res.now))
323
+ formatted_runs = _format_runs(run_dict, res.now)
324
+ if output_format == CliOutputFormat.JSON:
325
+ Console().print_json(_to_json(formatted_runs))
326
+ else:
327
+ Console().print(_to_table(formatted_runs))
flwr/cli/new/__init__.py CHANGED
@@ -14,6 +14,7 @@
14
14
  # ==============================================================================
15
15
  """Flower command line interface `new` command."""
16
16
 
17
+
17
18
  from .new import new as new
18
19
 
19
20
  __all__ = [
flwr/cli/new/new.py CHANGED
@@ -14,6 +14,7 @@
14
14
  # ==============================================================================
15
15
  """Flower command line interface `new` command."""
16
16
 
17
+
17
18
  import re
18
19
  from enum import Enum
19
20
  from pathlib import Path
@@ -81,7 +82,7 @@ def render_template(template: str, data: dict[str, str]) -> str:
81
82
  def create_file(file_path: Path, content: str) -> None:
82
83
  """Create file including all nessecary directories and write content into file."""
83
84
  file_path.parent.mkdir(exist_ok=True)
84
- file_path.write_text(content)
85
+ file_path.write_text(content, encoding="utf-8")
85
86
 
86
87
 
87
88
  def render_and_create(file_path: Path, template: str, context: dict[str, str]) -> None:
@@ -268,20 +269,30 @@ def new(
268
269
  context=context,
269
270
  )
270
271
 
271
- print(
272
- typer.style(
273
- "🎊 Flower App creation successful.\n\n"
274
- "Use the following command to run your Flower App:\n",
275
- fg=typer.colors.GREEN,
276
- bold=True,
277
- )
272
+ prompt = typer.style(
273
+ "🎊 Flower App creation successful.\n\n"
274
+ "To run your Flower App, use the following command:\n\n",
275
+ fg=typer.colors.GREEN,
276
+ bold=True,
278
277
  )
279
278
 
280
279
  _add = " huggingface-cli login\n" if llm_challenge_str else ""
281
- print(
282
- typer.style(
283
- f" cd {package_name}\n" + " pip install -e .\n" + _add + " flwr run\n",
284
- fg=typer.colors.BRIGHT_CYAN,
285
- bold=True,
286
- )
280
+ prompt += typer.style(
281
+ _add + f" flwr run {package_name}\n\n",
282
+ fg=typer.colors.BRIGHT_CYAN,
283
+ bold=True,
284
+ )
285
+
286
+ prompt += typer.style(
287
+ "If you haven't installed all dependencies yet, follow these steps:\n\n",
288
+ fg=typer.colors.GREEN,
289
+ bold=True,
287
290
  )
291
+
292
+ prompt += typer.style(
293
+ f" cd {package_name}\n" + " pip install -e .\n" + _add + " flwr run .\n",
294
+ fg=typer.colors.BRIGHT_CYAN,
295
+ bold=True,
296
+ )
297
+
298
+ print(prompt)
@@ -3,6 +3,9 @@ __pycache__/
3
3
  *.py[cod]
4
4
  *$py.class
5
5
 
6
+ # Flower directory
7
+ .flwr
8
+
6
9
  # C extensions
7
10
  *.so
8
11
 
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.1",
12
12
  "flwr-datasets[vision]>=0.3.0",
13
13
  "torch==2.2.1",
14
14
  "torchvision==0.17.1",
@@ -8,14 +8,14 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.1",
12
12
  "flwr-datasets>=0.3.0",
13
13
  "torch==2.3.1",
14
14
  "trl==0.8.1",
15
- "bitsandbytes==0.43.0",
15
+ "bitsandbytes==0.45.0",
16
16
  "scipy==1.13.0",
17
17
  "peft==0.6.2",
18
- "transformers==4.43.1",
18
+ "transformers==4.47.0",
19
19
  "sentencepiece==0.2.0",
20
20
  "omegaconf==2.3.0",
21
21
  "hf_transfer==0.1.8",
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.1",
12
12
  "flwr-datasets>=0.3.0",
13
13
  "torch==2.2.1",
14
14
  "transformers>=4.30.0,<5.0",
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.1",
12
12
  "jax==0.4.30",
13
13
  "jaxlib==0.4.30",
14
14
  "scikit-learn==1.3.2",
@@ -8,10 +8,9 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.1",
12
12
  "flwr-datasets[vision]>=0.3.0",
13
- "mlx==0.16.1",
14
- "numpy==1.24.4",
13
+ "mlx==0.21.1",
15
14
  ]
16
15
 
17
16
  [tool.hatch.build.targets.wheel]
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.1",
12
12
  "numpy>=1.21.0",
13
13
  ]
14
14
 
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.1",
12
12
  "flwr-datasets[vision]>=0.3.0",
13
13
  "torch==2.2.1",
14
14
  "torchvision==0.17.1",
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.1",
12
12
  "flwr-datasets[vision]>=0.3.0",
13
13
  "scikit-learn>=1.1.1",
14
14
  ]
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.1",
12
12
  "flwr-datasets[vision]>=0.3.0",
13
13
  "tensorflow>=2.11.1,<2.18.0",
14
14
  ]
flwr/cli/run/__init__.py CHANGED
@@ -14,6 +14,7 @@
14
14
  # ==============================================================================
15
15
  """Flower command line interface `run` command."""
16
16
 
17
+
17
18
  from .run import run as run
18
19
 
19
20
  __all__ = [