flwr 1.13.1__py3-none-any.whl → 1.15.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 (158) hide show
  1. flwr/cli/app.py +5 -0
  2. flwr/cli/auth_plugin/__init__.py +31 -0
  3. flwr/cli/auth_plugin/oidc_cli_plugin.py +150 -0
  4. flwr/cli/build.py +1 -0
  5. flwr/cli/cli_user_auth_interceptor.py +90 -0
  6. flwr/cli/config_utils.py +43 -149
  7. flwr/cli/constant.py +27 -0
  8. flwr/cli/example.py +1 -0
  9. flwr/cli/install.py +2 -1
  10. flwr/cli/log.py +34 -37
  11. flwr/cli/login/__init__.py +22 -0
  12. flwr/cli/login/login.py +116 -0
  13. flwr/cli/ls.py +214 -106
  14. flwr/cli/new/__init__.py +1 -0
  15. flwr/cli/new/new.py +2 -1
  16. flwr/cli/new/templates/app/.gitignore.tpl +3 -0
  17. flwr/cli/new/templates/app/README.md.tpl +3 -2
  18. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
  19. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
  20. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
  21. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +2 -2
  22. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +3 -4
  23. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +2 -2
  24. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +4 -4
  25. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +3 -3
  26. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +2 -2
  27. flwr/cli/run/__init__.py +1 -0
  28. flwr/cli/run/run.py +103 -43
  29. flwr/cli/stop.py +139 -0
  30. flwr/cli/utils.py +186 -8
  31. flwr/client/app.py +49 -50
  32. flwr/client/client.py +1 -32
  33. flwr/client/clientapp/app.py +23 -26
  34. flwr/client/clientapp/utils.py +2 -1
  35. flwr/client/grpc_adapter_client/connection.py +1 -1
  36. flwr/client/grpc_client/connection.py +2 -13
  37. flwr/client/grpc_rere_client/client_interceptor.py +19 -119
  38. flwr/client/grpc_rere_client/connection.py +59 -43
  39. flwr/client/grpc_rere_client/grpc_adapter.py +12 -12
  40. flwr/client/message_handler/message_handler.py +1 -2
  41. flwr/client/message_handler/task_handler.py +0 -17
  42. flwr/client/mod/comms_mods.py +1 -0
  43. flwr/client/mod/localdp_mod.py +1 -1
  44. flwr/client/nodestate/__init__.py +1 -0
  45. flwr/client/nodestate/nodestate.py +1 -0
  46. flwr/client/nodestate/nodestate_factory.py +1 -0
  47. flwr/client/numpy_client.py +0 -44
  48. flwr/client/rest_client/connection.py +37 -29
  49. flwr/client/supernode/app.py +20 -74
  50. flwr/common/address.py +1 -0
  51. flwr/common/args.py +26 -47
  52. flwr/common/auth_plugin/__init__.py +24 -0
  53. flwr/common/auth_plugin/auth_plugin.py +122 -0
  54. flwr/common/config.py +169 -17
  55. flwr/common/constant.py +38 -9
  56. flwr/common/differential_privacy.py +2 -1
  57. flwr/common/exit/__init__.py +24 -0
  58. flwr/common/exit/exit.py +99 -0
  59. flwr/common/exit/exit_code.py +93 -0
  60. flwr/common/exit_handlers.py +24 -10
  61. flwr/common/grpc.py +167 -4
  62. flwr/common/logger.py +66 -7
  63. flwr/common/message.py +1 -0
  64. flwr/common/object_ref.py +57 -54
  65. flwr/common/pyproject.py +1 -0
  66. flwr/common/record/__init__.py +1 -0
  67. flwr/common/record/parametersrecord.py +1 -0
  68. flwr/common/record/recordset.py +1 -1
  69. flwr/common/retry_invoker.py +77 -0
  70. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +45 -0
  71. flwr/common/secure_aggregation/secaggplus_utils.py +2 -2
  72. flwr/common/serde.py +6 -4
  73. flwr/common/telemetry.py +15 -4
  74. flwr/common/typing.py +32 -0
  75. flwr/common/version.py +1 -0
  76. flwr/proto/clientappio_pb2.py +1 -1
  77. flwr/proto/error_pb2.py +1 -1
  78. flwr/proto/exec_pb2.py +27 -15
  79. flwr/proto/exec_pb2.pyi +80 -2
  80. flwr/proto/exec_pb2_grpc.py +102 -0
  81. flwr/proto/exec_pb2_grpc.pyi +39 -0
  82. flwr/proto/fab_pb2.py +5 -5
  83. flwr/proto/fab_pb2.pyi +4 -1
  84. flwr/proto/fleet_pb2.py +31 -31
  85. flwr/proto/fleet_pb2.pyi +23 -23
  86. flwr/proto/fleet_pb2_grpc.py +30 -30
  87. flwr/proto/fleet_pb2_grpc.pyi +20 -20
  88. flwr/proto/grpcadapter_pb2.py +1 -1
  89. flwr/proto/log_pb2.py +1 -1
  90. flwr/proto/message_pb2.py +1 -1
  91. flwr/proto/node_pb2.py +3 -3
  92. flwr/proto/node_pb2.pyi +1 -4
  93. flwr/proto/recordset_pb2.py +1 -1
  94. flwr/proto/run_pb2.py +1 -1
  95. flwr/proto/serverappio_pb2.py +24 -25
  96. flwr/proto/serverappio_pb2.pyi +32 -32
  97. flwr/proto/serverappio_pb2_grpc.py +62 -28
  98. flwr/proto/serverappio_pb2_grpc.pyi +29 -16
  99. flwr/proto/simulationio_pb2.py +3 -3
  100. flwr/proto/simulationio_pb2_grpc.py +34 -0
  101. flwr/proto/simulationio_pb2_grpc.pyi +13 -0
  102. flwr/proto/task_pb2.py +1 -1
  103. flwr/proto/transport_pb2.py +1 -1
  104. flwr/server/app.py +152 -112
  105. flwr/server/compat/app_utils.py +7 -2
  106. flwr/server/compat/driver_client_proxy.py +1 -2
  107. flwr/server/driver/grpc_driver.py +38 -85
  108. flwr/server/driver/inmemory_driver.py +7 -2
  109. flwr/server/run_serverapp.py +8 -9
  110. flwr/server/serverapp/app.py +37 -13
  111. flwr/server/strategy/dpfedavg_fixed.py +1 -0
  112. flwr/server/superlink/driver/serverappio_grpc.py +2 -1
  113. flwr/server/superlink/driver/serverappio_servicer.py +148 -63
  114. flwr/server/superlink/ffs/disk_ffs.py +1 -0
  115. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +20 -87
  116. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -0
  117. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +2 -165
  118. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +56 -35
  119. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +99 -169
  120. flwr/server/superlink/fleet/message_handler/message_handler.py +69 -29
  121. flwr/server/superlink/fleet/rest_rere/rest_api.py +20 -19
  122. flwr/server/superlink/fleet/vce/__init__.py +1 -0
  123. flwr/server/superlink/fleet/vce/backend/__init__.py +1 -0
  124. flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -0
  125. flwr/server/superlink/fleet/vce/vce_api.py +2 -2
  126. flwr/server/superlink/linkstate/in_memory_linkstate.py +60 -99
  127. flwr/server/superlink/linkstate/linkstate.py +30 -36
  128. flwr/server/superlink/linkstate/sqlite_linkstate.py +105 -188
  129. flwr/server/superlink/linkstate/utils.py +18 -8
  130. flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
  131. flwr/server/superlink/simulation/simulationio_servicer.py +33 -0
  132. flwr/server/superlink/utils.py +65 -0
  133. flwr/server/utils/validator.py +9 -34
  134. flwr/simulation/app.py +20 -10
  135. flwr/simulation/legacy_app.py +4 -2
  136. flwr/simulation/ray_transport/ray_actor.py +1 -0
  137. flwr/simulation/ray_transport/utils.py +1 -0
  138. flwr/simulation/run_simulation.py +36 -22
  139. flwr/simulation/simulationio_connection.py +5 -1
  140. flwr/superexec/app.py +1 -0
  141. flwr/superexec/deployment.py +1 -0
  142. flwr/superexec/exec_grpc.py +20 -2
  143. flwr/superexec/exec_servicer.py +97 -2
  144. flwr/superexec/exec_user_auth_interceptor.py +101 -0
  145. flwr/superexec/executor.py +1 -0
  146. {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/METADATA +14 -13
  147. {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/RECORD +150 -144
  148. flwr/proto/common_pb2.py +0 -36
  149. flwr/proto/common_pb2.pyi +0 -121
  150. flwr/proto/common_pb2_grpc.py +0 -4
  151. flwr/proto/common_pb2_grpc.pyi +0 -4
  152. flwr/proto/control_pb2.py +0 -27
  153. flwr/proto/control_pb2.pyi +0 -7
  154. flwr/proto/control_pb2_grpc.py +0 -135
  155. flwr/proto/control_pb2_grpc.pyi +0 -53
  156. {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/LICENSE +0 -0
  157. {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/WHEEL +0 -0
  158. {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/entry_points.txt +0 -0
flwr/cli/ls.py CHANGED
@@ -15,27 +15,27 @@
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.cli.constant import FEDERATION_CONFIG_HELP_MESSAGE
36
+ from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat, SubStatus
36
37
  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
38
+ from flwr.common.logger import print_json_error, redirect_output, restore_output
39
39
  from flwr.common.serde import run_from_proto
40
40
  from flwr.common.typing import Run
41
41
  from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
@@ -44,8 +44,12 @@ from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
44
44
  )
45
45
  from flwr.proto.exec_pb2_grpc import ExecStub
46
46
 
47
+ from .utils import init_channel, try_obtain_cli_auth_plugin, unauthenticated_exc_handler
47
48
 
48
- def ls(
49
+ _RunListType = tuple[int, str, str, str, str, str, str, str, str]
50
+
51
+
52
+ def ls( # pylint: disable=too-many-locals, too-many-branches, R0913, R0917
49
53
  app: Annotated[
50
54
  Path,
51
55
  typer.Argument(help="Path of the Flower project"),
@@ -54,6 +58,13 @@ def ls(
54
58
  Optional[str],
55
59
  typer.Argument(help="Name of the federation"),
56
60
  ] = None,
61
+ federation_config_overrides: Annotated[
62
+ Optional[list[str]],
63
+ typer.Option(
64
+ "--federation-config",
65
+ help=FEDERATION_CONFIG_HELP_MESSAGE,
66
+ ),
67
+ ] = None,
57
68
  runs: Annotated[
58
69
  bool,
59
70
  typer.Option(
@@ -68,94 +79,101 @@ def ls(
68
79
  help="Specific run ID to display",
69
80
  ),
70
81
  ] = None,
82
+ output_format: Annotated[
83
+ str,
84
+ typer.Option(
85
+ "--format",
86
+ case_sensitive=False,
87
+ help="Format output using 'default' view or 'json'",
88
+ ),
89
+ ] = CliOutputFormat.DEFAULT,
71
90
  ) -> 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 ls` 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
-
91
+ """List the details of one provided run ID or all runs in a Flower federation.
92
+
93
+ The following details are displayed:
94
+
95
+ - **Run ID:** Unique identifier for the run.
96
+ - **FAB:** Name of the FAB associated with the run (``{FAB_ID} (v{FAB_VERSION})``).
97
+ - **Status:** Current status of the run (pending, starting, running, finished).
98
+ - **Elapsed:** Time elapsed since the run started (``HH:MM:SS``).
99
+ - **Created At:** Timestamp when the run was created.
100
+ - **Running At:** Timestamp when the run started running.
101
+ - **Finished At:** Timestamp when the run finished.
102
+
103
+ All timestamps follow ISO 8601, UTC and are formatted as ``YYYY-MM-DD HH:MM:SSZ``.
104
+ """
105
+ suppress_output = output_format == CliOutputFormat.JSON
106
+ captured_output = io.StringIO()
92
107
  try:
93
- if runs and run_id is not None:
94
- raise ValueError(
95
- "The options '--runs' and '--run-id' are mutually exclusive."
108
+ if suppress_output:
109
+ redirect_output(captured_output)
110
+ # Load and validate federation config
111
+ typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
112
+
113
+ pyproject_path = app / FAB_CONFIG_FILE if app else None
114
+ config, errors, warnings = load_and_validate(path=pyproject_path)
115
+ config = process_loaded_project_config(config, errors, warnings)
116
+ federation, federation_config = validate_federation_in_project_config(
117
+ federation, config, federation_config_overrides
118
+ )
119
+ exit_if_no_address(federation_config, "ls")
120
+ channel = None
121
+ try:
122
+ if runs and run_id is not None:
123
+ raise ValueError(
124
+ "The options '--runs' and '--run-id' are mutually exclusive."
125
+ )
126
+ auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
127
+ channel = init_channel(app, federation_config, auth_plugin)
128
+ stub = ExecStub(channel)
129
+
130
+ # Display information about a specific run ID
131
+ if run_id is not None:
132
+ typer.echo(f"🔍 Displaying information for run ID {run_id}...")
133
+ restore_output()
134
+ _display_one_run(stub, run_id, output_format)
135
+ # By default, list all runs
136
+ else:
137
+ typer.echo("📄 Listing all runs...")
138
+ restore_output()
139
+ _list_runs(stub, output_format)
140
+
141
+ except ValueError as err:
142
+ if suppress_output:
143
+ redirect_output(captured_output)
144
+ typer.secho(
145
+ f"❌ {err}",
146
+ fg=typer.colors.RED,
147
+ bold=True,
96
148
  )
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
149
+ raise typer.Exit(code=1) from err
150
+ finally:
151
+ if channel:
152
+ channel.close()
153
+ except (typer.Exit, Exception) as err: # pylint: disable=broad-except
154
+ if suppress_output:
155
+ restore_output()
156
+ e_message = captured_output.getvalue()
157
+ print_json_error(e_message, err)
106
158
  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
159
+ typer.secho(
160
+ f"{err}",
161
+ fg=typer.colors.RED,
162
+ bold=True,
163
+ )
117
164
  finally:
118
- channel.close()
119
-
165
+ if suppress_output:
166
+ restore_output()
167
+ captured_output.close()
120
168
 
121
- def on_channel_state_change(channel_connectivity: str) -> None:
122
- """Log channel connectivity."""
123
- log(DEBUG, channel_connectivity)
124
169
 
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)
170
+ def _format_runs(run_dict: dict[int, Run], now_isoformat: str) -> list[_RunListType]:
171
+ """Format runs to a list."""
145
172
 
146
173
  def _format_datetime(dt: Optional[datetime]) -> str:
147
174
  return isoformat8601_utc(dt).replace("T", " ") if dt else "N/A"
148
175
 
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")
176
+ run_list: list[_RunListType] = []
159
177
 
160
178
  # Add rows
161
179
  for run in sorted(
@@ -167,15 +185,6 @@ def _format_run_table(run_dict: dict[int, Run], now_isoformat: str) -> Table:
167
185
  else:
168
186
  status_text = f"{run.status.status}:{run.status.sub_status}"
169
187
 
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
188
  # Convert isoformat to datetime
180
189
  pending_at = datetime.fromisoformat(run.pending_at) if run.pending_at else None
181
190
  running_at = datetime.fromisoformat(run.running_at) if run.running_at else None
@@ -192,37 +201,136 @@ def _format_run_table(run_dict: dict[int, Run], now_isoformat: str) -> Table:
192
201
  end_time = datetime.fromisoformat(now_isoformat)
193
202
  elapsed_time = end_time - running_at
194
203
 
195
- table.add_row(
196
- f"[bold]{run.run_id}[/bold]",
197
- f"{run.fab_id} (v{run.fab_version})",
204
+ run_list.append(
205
+ (
206
+ run.run_id,
207
+ run.fab_id,
208
+ run.fab_version,
209
+ run.fab_hash,
210
+ status_text,
211
+ format_timedelta(elapsed_time),
212
+ _format_datetime(pending_at),
213
+ _format_datetime(running_at),
214
+ _format_datetime(finished_at),
215
+ )
216
+ )
217
+ return run_list
218
+
219
+
220
+ def _to_table(run_list: list[_RunListType]) -> Table:
221
+ """Format the provided run list to a rich Table."""
222
+ table = Table(header_style="bold cyan", show_lines=True)
223
+
224
+ # Add columns
225
+ table.add_column(
226
+ Text("Run ID", justify="center"), style="bright_white", overflow="fold"
227
+ )
228
+ table.add_column(Text("FAB", justify="center"), style="dim white")
229
+ table.add_column(Text("Status", justify="center"))
230
+ table.add_column(Text("Elapsed", justify="center"), style="blue")
231
+ table.add_column(Text("Created At", justify="center"), style="dim white")
232
+ table.add_column(Text("Running At", justify="center"), style="dim white")
233
+ table.add_column(Text("Finished At", justify="center"), style="dim white")
234
+
235
+ for row in run_list:
236
+ (
237
+ run_id,
238
+ fab_id,
239
+ fab_version,
240
+ _,
241
+ status_text,
242
+ elapsed,
243
+ created_at,
244
+ running_at,
245
+ finished_at,
246
+ ) = row
247
+ # Style the status based on its value
248
+ sub_status = status_text.rsplit(":", maxsplit=1)[-1]
249
+ if sub_status == SubStatus.COMPLETED:
250
+ status_style = "green"
251
+ elif sub_status == SubStatus.FAILED:
252
+ status_style = "red"
253
+ else:
254
+ status_style = "yellow"
255
+
256
+ formatted_row = (
257
+ f"[bold]{run_id}[/bold]",
258
+ f"{fab_id} (v{fab_version})",
198
259
  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),
260
+ elapsed,
261
+ created_at,
262
+ running_at,
263
+ finished_at,
203
264
  )
265
+ table.add_row(*formatted_row)
266
+
204
267
  return table
205
268
 
206
269
 
270
+ def _to_json(run_list: list[_RunListType]) -> str:
271
+ """Format run status list to a JSON formatted string."""
272
+ runs_list = []
273
+ for row in run_list:
274
+ (
275
+ run_id,
276
+ fab_id,
277
+ fab_version,
278
+ fab_hash,
279
+ status_text,
280
+ elapsed,
281
+ created_at,
282
+ running_at,
283
+ finished_at,
284
+ ) = row
285
+ runs_list.append(
286
+ {
287
+ "run-id": run_id,
288
+ "fab-id": fab_id,
289
+ "fab-name": fab_id.split("/")[-1],
290
+ "fab-version": fab_version,
291
+ "fab-hash": fab_hash[:8],
292
+ "status": status_text,
293
+ "elapsed": elapsed,
294
+ "created-at": created_at,
295
+ "running-at": running_at,
296
+ "finished-at": finished_at,
297
+ }
298
+ )
299
+
300
+ return json.dumps({"success": True, "runs": runs_list})
301
+
302
+
207
303
  def _list_runs(
208
304
  stub: ExecStub,
305
+ output_format: str = CliOutputFormat.DEFAULT,
209
306
  ) -> None:
210
307
  """List all runs."""
211
- res: ListRunsResponse = stub.ListRuns(ListRunsRequest())
308
+ with unauthenticated_exc_handler():
309
+ res: ListRunsResponse = stub.ListRuns(ListRunsRequest())
212
310
  run_dict = {run_id: run_from_proto(proto) for run_id, proto in res.run_dict.items()}
213
311
 
214
- Console().print(_format_run_table(run_dict, res.now))
312
+ formatted_runs = _format_runs(run_dict, res.now)
313
+ if output_format == CliOutputFormat.JSON:
314
+ Console().print_json(_to_json(formatted_runs))
315
+ else:
316
+ Console().print(_to_table(formatted_runs))
215
317
 
216
318
 
217
319
  def _display_one_run(
218
320
  stub: ExecStub,
219
321
  run_id: int,
322
+ output_format: str = CliOutputFormat.DEFAULT,
220
323
  ) -> None:
221
324
  """Display information about a specific run."""
222
- res: ListRunsResponse = stub.ListRuns(ListRunsRequest(run_id=run_id))
325
+ with unauthenticated_exc_handler():
326
+ res: ListRunsResponse = stub.ListRuns(ListRunsRequest(run_id=run_id))
223
327
  if not res.run_dict:
224
328
  raise ValueError(f"Run ID {run_id} not found")
225
329
 
226
330
  run_dict = {run_id: run_from_proto(proto) for run_id, proto in res.run_dict.items()}
227
331
 
228
- Console().print(_format_run_table(run_dict, res.now))
332
+ formatted_runs = _format_runs(run_dict, res.now)
333
+ if output_format == CliOutputFormat.JSON:
334
+ Console().print_json(_to_json(formatted_runs))
335
+ else:
336
+ 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:
@@ -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
 
@@ -18,8 +18,9 @@ Refer to the [How to Run Simulations](https://flower.ai/docs/framework/how-to-ru
18
18
 
19
19
  ## Run with the Deployment Engine
20
20
 
21
- > \[!NOTE\]
22
- > An update to this example will show how to run this Flower application with the Deployment Engine and TLS certificates, or with Docker.
21
+ Follow this [how-to guide](https://flower.ai/docs/framework/how-to-run-flower-with-deployment-engine.html) to run the same app in this example but with Flower's Deployment Engine. After that, you might be intersted in setting up [secure TLS-enabled communications](https://flower.ai/docs/framework/how-to-enable-tls-connections.html) and [SuperNode authentication](https://flower.ai/docs/framework/how-to-authenticate-supernodes.html) in your federation.
22
+
23
+ You can run Flower on Docker too! Check out the [Flower with Docker](https://flower.ai/docs/framework/docker/index.html) documentation.
23
24
 
24
25
  ## Resources
25
26
 
@@ -8,10 +8,10 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.13.0",
12
- "flwr-datasets[vision]>=0.3.0",
13
- "torch==2.2.1",
14
- "torchvision==0.17.1",
11
+ "flwr[simulation]>=1.15.0",
12
+ "flwr-datasets[vision]>=0.5.0",
13
+ "torch==2.5.1",
14
+ "torchvision==0.20.1",
15
15
  ]
16
16
 
17
17
  [tool.hatch.metadata]
@@ -8,14 +8,14 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.13.0",
12
- "flwr-datasets>=0.3.0",
11
+ "flwr[simulation]>=1.15.0",
12
+ "flwr-datasets>=0.5.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,13 +8,13 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.13.0",
12
- "flwr-datasets>=0.3.0",
13
- "torch==2.2.1",
11
+ "flwr[simulation]>=1.15.0",
12
+ "flwr-datasets>=0.5.0",
13
+ "torch==2.5.1",
14
14
  "transformers>=4.30.0,<5.0",
15
15
  "evaluate>=0.4.0,<1.0",
16
16
  "datasets>=2.0.0, <3.0",
17
- "scikit-learn>=1.3.1, <2.0",
17
+ "scikit-learn>=1.6.1, <2.0",
18
18
  ]
19
19
 
20
20
  [tool.hatch.build.targets.wheel]
@@ -8,10 +8,10 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.13.0",
11
+ "flwr[simulation]>=1.15.0",
12
12
  "jax==0.4.30",
13
13
  "jaxlib==0.4.30",
14
- "scikit-learn==1.3.2",
14
+ "scikit-learn==1.6.1",
15
15
  ]
16
16
 
17
17
  [tool.hatch.build.targets.wheel]
@@ -8,10 +8,9 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.13.0",
12
- "flwr-datasets[vision]>=0.3.0",
13
- "mlx==0.16.1",
14
- "numpy==1.24.4",
11
+ "flwr[simulation]>=1.15.0",
12
+ "flwr-datasets[vision]>=0.5.0",
13
+ "mlx==0.21.1",
15
14
  ]
16
15
 
17
16
  [tool.hatch.build.targets.wheel]
@@ -8,8 +8,8 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.13.0",
12
- "numpy>=1.21.0",
11
+ "flwr[simulation]>=1.15.0",
12
+ "numpy>=2.0.2",
13
13
  ]
14
14
 
15
15
  [tool.hatch.build.targets.wheel]
@@ -8,10 +8,10 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.13.0",
12
- "flwr-datasets[vision]>=0.3.0",
13
- "torch==2.2.1",
14
- "torchvision==0.17.1",
11
+ "flwr[simulation]>=1.15.0",
12
+ "flwr-datasets[vision]>=0.5.0",
13
+ "torch==2.5.1",
14
+ "torchvision==0.20.1",
15
15
  ]
16
16
 
17
17
  [tool.hatch.build.targets.wheel]
@@ -8,9 +8,9 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.13.0",
12
- "flwr-datasets[vision]>=0.3.0",
13
- "scikit-learn>=1.1.1",
11
+ "flwr[simulation]>=1.15.0",
12
+ "flwr-datasets[vision]>=0.5.0",
13
+ "scikit-learn>=1.6.1",
14
14
  ]
15
15
 
16
16
  [tool.hatch.build.targets.wheel]
@@ -8,8 +8,8 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.13.0",
12
- "flwr-datasets[vision]>=0.3.0",
11
+ "flwr[simulation]>=1.15.0",
12
+ "flwr-datasets[vision]>=0.5.0",
13
13
  "tensorflow>=2.11.1,<2.18.0",
14
14
  ]
15
15
 
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__ = [