flwr-nightly 1.26.0.dev20260123__py3-none-any.whl → 1.26.0.dev20260126__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/cli/app_cmd/publish.py +18 -44
- flwr/cli/app_cmd/review.py +8 -25
- flwr/cli/auth_plugin/oidc_cli_plugin.py +3 -6
- flwr/cli/build.py +8 -19
- flwr/cli/config/ls.py +8 -13
- flwr/cli/config_utils.py +19 -39
- flwr/cli/federation/ls.py +3 -7
- flwr/cli/flower_config.py +28 -47
- flwr/cli/install.py +18 -57
- flwr/cli/log.py +2 -2
- flwr/cli/login/login.py +8 -21
- flwr/cli/ls.py +3 -7
- flwr/cli/new/new.py +9 -29
- flwr/cli/pull.py +3 -7
- flwr/cli/run/run.py +6 -15
- flwr/cli/stop.py +5 -17
- flwr/cli/supernode/register.py +6 -22
- flwr/cli/supernode/unregister.py +3 -13
- flwr/cli/utils.py +64 -114
- flwr/common/config.py +5 -9
- flwr/common/constant.py +2 -0
- flwr/server/superlink/fleet/message_handler/message_handler.py +4 -4
- flwr/server/superlink/linkstate/sql_linkstate.py +38 -10
- flwr/superlink/servicer/control/control_servicer.py +11 -12
- {flwr_nightly-1.26.0.dev20260123.dist-info → flwr_nightly-1.26.0.dev20260126.dist-info}/METADATA +1 -1
- {flwr_nightly-1.26.0.dev20260123.dist-info → flwr_nightly-1.26.0.dev20260126.dist-info}/RECORD +28 -28
- {flwr_nightly-1.26.0.dev20260123.dist-info → flwr_nightly-1.26.0.dev20260126.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.26.0.dev20260123.dist-info → flwr_nightly-1.26.0.dev20260126.dist-info}/entry_points.txt +0 -0
flwr/cli/utils.py
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import hashlib
|
|
19
19
|
import json
|
|
20
|
+
import re
|
|
20
21
|
from collections.abc import Callable, Iterable, Iterator
|
|
21
22
|
from contextlib import contextmanager
|
|
22
23
|
from pathlib import Path
|
|
@@ -31,6 +32,8 @@ from flwr.cli.typing import SuperLinkConnection
|
|
|
31
32
|
from flwr.common.constant import (
|
|
32
33
|
ACCESS_TOKEN_KEY,
|
|
33
34
|
AUTHN_TYPE_JSON_KEY,
|
|
35
|
+
FEDERATION_NOT_FOUND_MESSAGE,
|
|
36
|
+
FEDERATION_NOT_SPECIFIED_MESSAGE,
|
|
34
37
|
NO_ACCOUNT_AUTH_MESSAGE,
|
|
35
38
|
NO_ARTIFACT_PROVIDER_MESSAGE,
|
|
36
39
|
NODE_NOT_FOUND_MESSAGE,
|
|
@@ -213,7 +216,7 @@ def load_cli_auth_plugin_from_connection(
|
|
|
213
216
|
|
|
214
217
|
Raises
|
|
215
218
|
------
|
|
216
|
-
|
|
219
|
+
click.ClickException
|
|
217
220
|
If the authentication type is unknown.
|
|
218
221
|
"""
|
|
219
222
|
# Determine the auth type if not provided
|
|
@@ -227,22 +230,19 @@ def load_cli_auth_plugin_from_connection(
|
|
|
227
230
|
auth_plugin_class = get_cli_plugin_class(authn_type)
|
|
228
231
|
return auth_plugin_class(host)
|
|
229
232
|
except ValueError:
|
|
230
|
-
|
|
231
|
-
|
|
233
|
+
raise click.ClickException(
|
|
234
|
+
f"Unknown account authentication type: {authn_type}"
|
|
235
|
+
) from None
|
|
232
236
|
|
|
233
237
|
|
|
234
238
|
def require_superlink_address(connection: SuperLinkConnection) -> str:
|
|
235
239
|
"""Return the SuperLink address or exit if it is not configured."""
|
|
236
240
|
if connection.address is None:
|
|
237
241
|
cmd = click.get_current_context().command.name
|
|
238
|
-
|
|
239
|
-
f"
|
|
240
|
-
"correct SuperLink (Control API) address is provided in `pyproject.toml`."
|
|
241
|
-
fg=typer.colors.RED,
|
|
242
|
-
bold=True,
|
|
243
|
-
err=True,
|
|
242
|
+
raise click.ClickException(
|
|
243
|
+
f"`flwr {cmd}` currently works with a SuperLink. Ensure that the "
|
|
244
|
+
"correct SuperLink (Control API) address is provided in `pyproject.toml`."
|
|
244
245
|
)
|
|
245
|
-
raise typer.Exit(code=1)
|
|
246
246
|
return connection.address
|
|
247
247
|
|
|
248
248
|
|
|
@@ -301,7 +301,7 @@ def flwr_cli_grpc_exc_handler() -> Iterator[None]: # pylint: disable=too-many-b
|
|
|
301
301
|
|
|
302
302
|
Raises
|
|
303
303
|
------
|
|
304
|
-
|
|
304
|
+
click.ClickException
|
|
305
305
|
On handled gRPC error statuses with appropriate exit code.
|
|
306
306
|
grpc.RpcError
|
|
307
307
|
For unhandled gRPC error statuses.
|
|
@@ -310,115 +310,71 @@ def flwr_cli_grpc_exc_handler() -> Iterator[None]: # pylint: disable=too-many-b
|
|
|
310
310
|
yield
|
|
311
311
|
except grpc.RpcError as e:
|
|
312
312
|
if e.code() == grpc.StatusCode.UNAUTHENTICATED:
|
|
313
|
-
|
|
314
|
-
"
|
|
315
|
-
" to authenticate and try again."
|
|
316
|
-
|
|
317
|
-
bold=True,
|
|
318
|
-
err=True,
|
|
319
|
-
)
|
|
320
|
-
raise typer.Exit(code=1) from None
|
|
313
|
+
raise click.ClickException(
|
|
314
|
+
"Authentication failed. Please run `flwr login`"
|
|
315
|
+
" to authenticate and try again."
|
|
316
|
+
) from None
|
|
321
317
|
if e.code() == grpc.StatusCode.UNIMPLEMENTED:
|
|
322
318
|
if e.details() == NO_ACCOUNT_AUTH_MESSAGE: # pylint: disable=E1101
|
|
323
|
-
|
|
324
|
-
"
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
else:
|
|
337
|
-
typer.secho(
|
|
338
|
-
"❌ The SuperLink cannot process this request. Please verify that "
|
|
339
|
-
"you set the address to its Control API endpoint correctly in your "
|
|
340
|
-
"`pyproject.toml`, and ensure that the Flower versions used by "
|
|
341
|
-
"the CLI and SuperLink are compatible.",
|
|
342
|
-
fg=typer.colors.RED,
|
|
343
|
-
bold=True,
|
|
344
|
-
err=True,
|
|
345
|
-
)
|
|
346
|
-
raise typer.Exit(code=1) from None
|
|
319
|
+
raise click.ClickException(
|
|
320
|
+
"Account authentication is not enabled on this SuperLink."
|
|
321
|
+
) from None
|
|
322
|
+
if e.details() == NO_ARTIFACT_PROVIDER_MESSAGE: # pylint: disable=E1101
|
|
323
|
+
raise click.ClickException(
|
|
324
|
+
"The SuperLink does not support `flwr pull` command."
|
|
325
|
+
) from None
|
|
326
|
+
raise click.ClickException(
|
|
327
|
+
"The SuperLink cannot process this request. Please verify that "
|
|
328
|
+
"you set the address to its Control API endpoint correctly in your "
|
|
329
|
+
"`pyproject.toml`, and ensure that the Flower versions used by "
|
|
330
|
+
"the CLI and SuperLink are compatible."
|
|
331
|
+
) from None
|
|
347
332
|
if e.code() == grpc.StatusCode.PERMISSION_DENIED:
|
|
348
|
-
typer.secho(
|
|
349
|
-
"❌ Permission denied.",
|
|
350
|
-
fg=typer.colors.RED,
|
|
351
|
-
bold=True,
|
|
352
|
-
err=True,
|
|
353
|
-
)
|
|
354
333
|
# pylint: disable-next=E1101
|
|
355
|
-
|
|
356
|
-
raise typer.Exit(code=1) from None
|
|
334
|
+
raise click.ClickException(f"Permission denied.\n{e.details()}") from None
|
|
357
335
|
if e.code() == grpc.StatusCode.UNAVAILABLE:
|
|
358
|
-
|
|
336
|
+
raise click.ClickException(
|
|
359
337
|
"Connection to the SuperLink is unavailable. Please check your network "
|
|
360
|
-
"connection and 'address' in the
|
|
361
|
-
|
|
362
|
-
bold=True,
|
|
363
|
-
err=True,
|
|
364
|
-
)
|
|
365
|
-
raise typer.Exit(code=1) from None
|
|
338
|
+
"connection and 'address' in the SuperLink connection configuration."
|
|
339
|
+
) from None
|
|
366
340
|
if e.code() == grpc.StatusCode.NOT_FOUND:
|
|
367
341
|
if e.details() == RUN_ID_NOT_FOUND_MESSAGE: # pylint: disable=E1101
|
|
368
|
-
|
|
369
|
-
"❌ Run ID not found.",
|
|
370
|
-
fg=typer.colors.RED,
|
|
371
|
-
bold=True,
|
|
372
|
-
err=True,
|
|
373
|
-
)
|
|
374
|
-
raise typer.Exit(code=1) from None
|
|
342
|
+
raise click.ClickException("Run ID not found.") from None
|
|
375
343
|
if e.details() == NODE_NOT_FOUND_MESSAGE: # pylint: disable=E1101
|
|
376
|
-
|
|
377
|
-
"
|
|
378
|
-
|
|
379
|
-
bold=True,
|
|
380
|
-
err=True,
|
|
381
|
-
)
|
|
382
|
-
raise typer.Exit(code=1) from None
|
|
344
|
+
raise click.ClickException(
|
|
345
|
+
"Node ID not found for this account."
|
|
346
|
+
) from None
|
|
383
347
|
if e.code() == grpc.StatusCode.FAILED_PRECONDITION:
|
|
384
348
|
if e.details() == PULL_UNFINISHED_RUN_MESSAGE: # pylint: disable=E1101
|
|
385
|
-
|
|
386
|
-
"
|
|
387
|
-
"the run is finished. You can check the run status with `flwr ls`."
|
|
388
|
-
|
|
389
|
-
bold=True,
|
|
390
|
-
err=True,
|
|
391
|
-
)
|
|
392
|
-
raise typer.Exit(code=1) from None
|
|
349
|
+
raise click.ClickException(
|
|
350
|
+
"Run is not finished yet. Artifacts can only be pulled after "
|
|
351
|
+
"the run is finished. You can check the run status with `flwr ls`."
|
|
352
|
+
) from None
|
|
393
353
|
if (
|
|
394
354
|
e.details() == PUBLIC_KEY_ALREADY_IN_USE_MESSAGE
|
|
395
355
|
): # pylint: disable=E1101
|
|
396
|
-
|
|
397
|
-
"
|
|
398
|
-
|
|
399
|
-
fg=typer.colors.RED,
|
|
400
|
-
bold=True,
|
|
401
|
-
err=True,
|
|
402
|
-
)
|
|
403
|
-
raise typer.Exit(code=1) from None
|
|
356
|
+
raise click.ClickException(
|
|
357
|
+
"The provided public key is already in use by another SuperNode."
|
|
358
|
+
) from None
|
|
404
359
|
if e.details() == PUBLIC_KEY_NOT_VALID: # pylint: disable=E1101
|
|
405
|
-
|
|
406
|
-
"
|
|
407
|
-
"NIST EC public key."
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
360
|
+
raise click.ClickException(
|
|
361
|
+
"The provided public key is invalid. Please provide a valid "
|
|
362
|
+
"NIST EC public key."
|
|
363
|
+
) from None
|
|
364
|
+
if e.details() == FEDERATION_NOT_SPECIFIED_MESSAGE: # pylint: disable=E1101
|
|
365
|
+
raise click.ClickException(
|
|
366
|
+
"No federation specified. "
|
|
367
|
+
"Please specify a federation and try again.",
|
|
368
|
+
) from None
|
|
369
|
+
patten = re.compile(FEDERATION_NOT_FOUND_MESSAGE.replace("%s", "(.+)"))
|
|
370
|
+
if m := patten.match(e.details()): # pylint: disable=E1101
|
|
371
|
+
raise click.ClickException(
|
|
372
|
+
f"Federation '{m.group(1)}' does not exist. "
|
|
373
|
+
"Please verify the federation name and try again."
|
|
374
|
+
) from None
|
|
413
375
|
|
|
414
376
|
# Log details from grpc error directly
|
|
415
|
-
|
|
416
|
-
f"❌ {e.details()}",
|
|
417
|
-
fg=typer.colors.RED,
|
|
418
|
-
bold=True,
|
|
419
|
-
err=True,
|
|
420
|
-
)
|
|
421
|
-
raise typer.Exit(code=1) from None
|
|
377
|
+
raise click.ClickException(f"{e.details()}") from None
|
|
422
378
|
raise
|
|
423
379
|
|
|
424
380
|
|
|
@@ -478,23 +434,17 @@ def validate_credentials_content(creds_path: Path) -> str:
|
|
|
478
434
|
try:
|
|
479
435
|
creds: dict[str, str] = json.loads(creds_path.read_text(encoding="utf-8"))
|
|
480
436
|
except (OSError, json.JSONDecodeError) as err:
|
|
481
|
-
|
|
482
|
-
f"Invalid credentials file at '{creds_path}': {err}"
|
|
483
|
-
|
|
484
|
-
err=True,
|
|
485
|
-
)
|
|
486
|
-
raise typer.Exit(code=1) from err
|
|
437
|
+
raise click.ClickException(
|
|
438
|
+
f"Invalid credentials file at '{creds_path}': {err}"
|
|
439
|
+
) from err
|
|
487
440
|
|
|
488
441
|
required_keys = [AUTHN_TYPE_JSON_KEY, ACCESS_TOKEN_KEY, REFRESH_TOKEN_KEY]
|
|
489
442
|
missing = [key for key in required_keys if key not in creds]
|
|
490
443
|
|
|
491
444
|
if missing:
|
|
492
|
-
|
|
445
|
+
raise click.ClickException(
|
|
493
446
|
f"Credentials file '{creds_path}' is missing "
|
|
494
|
-
f"required key(s): {', '.join(missing)}. Please log in again."
|
|
495
|
-
fg=typer.colors.RED,
|
|
496
|
-
err=True,
|
|
447
|
+
f"required key(s): {', '.join(missing)}. Please log in again."
|
|
497
448
|
)
|
|
498
|
-
raise typer.Exit(code=1)
|
|
499
449
|
|
|
500
450
|
return creds[ACCESS_TOKEN_KEY]
|
flwr/common/config.py
CHANGED
|
@@ -22,8 +22,8 @@ from io import BytesIO
|
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from typing import IO, Any, TypeVar, cast, get_args
|
|
24
24
|
|
|
25
|
+
import click
|
|
25
26
|
import tomli
|
|
26
|
-
import typer
|
|
27
27
|
|
|
28
28
|
from flwr.app.user_config import UserConfig, UserConfigValue
|
|
29
29
|
from flwr.common.constant import (
|
|
@@ -235,17 +235,13 @@ def parse_config_args(config: list[str] | None, flatten: bool = True) -> dict[st
|
|
|
235
235
|
overrides.update(tomli.loads(toml_str))
|
|
236
236
|
flat_overrides = flatten_dict(overrides) if flatten else overrides
|
|
237
237
|
except tomli.TOMLDecodeError as err:
|
|
238
|
-
|
|
239
|
-
"
|
|
238
|
+
raise click.ClickException(
|
|
239
|
+
"The provided configuration string is in an invalid format. "
|
|
240
240
|
"The correct format should be, e.g., 'key1=123 key2=false "
|
|
241
241
|
'key3="string"\', where values must be of type bool, int, '
|
|
242
242
|
"string, or float. Ensure proper formatting with "
|
|
243
|
-
"space-separated key-value pairs."
|
|
244
|
-
|
|
245
|
-
bold=True,
|
|
246
|
-
err=True,
|
|
247
|
-
)
|
|
248
|
-
raise typer.Exit(code=1) from err
|
|
243
|
+
"space-separated key-value pairs."
|
|
244
|
+
) from err
|
|
249
245
|
|
|
250
246
|
return flat_overrides
|
|
251
247
|
|
flwr/common/constant.py
CHANGED
|
@@ -180,6 +180,8 @@ SUPERNODE_NOT_CREATED_FROM_CLI_MESSAGE = "Invalid SuperNode credentials"
|
|
|
180
180
|
PUBLIC_KEY_ALREADY_IN_USE_MESSAGE = "Public key already in use"
|
|
181
181
|
PUBLIC_KEY_NOT_VALID = "The provided public key is not valid"
|
|
182
182
|
NODE_NOT_FOUND_MESSAGE = "Node ID not found for account"
|
|
183
|
+
FEDERATION_NOT_SPECIFIED_MESSAGE = "No federation specified in the request"
|
|
184
|
+
FEDERATION_NOT_FOUND_MESSAGE = "Federation '%s' does not exist"
|
|
183
185
|
|
|
184
186
|
|
|
185
187
|
class MessageTypeLegacy:
|
|
@@ -166,13 +166,13 @@ def pull_messages( # pylint: disable=too-many-locals
|
|
|
166
166
|
response = PullMessagesResponse(messages_list=msg_proto, message_object_trees=trees)
|
|
167
167
|
|
|
168
168
|
# Record incoming traffic size
|
|
169
|
-
bytes_recv =
|
|
169
|
+
bytes_recv = request.ByteSize()
|
|
170
170
|
|
|
171
171
|
# Record traffic only if message was successfully processed
|
|
172
172
|
# All messages in this request are assumed to belong to the same run
|
|
173
173
|
if run_id_to_record is not None:
|
|
174
174
|
# Record outgoing traffic size
|
|
175
|
-
bytes_sent =
|
|
175
|
+
bytes_sent = response.ByteSize()
|
|
176
176
|
state.store_traffic(
|
|
177
177
|
run_id_to_record, bytes_sent=bytes_sent, bytes_recv=bytes_recv
|
|
178
178
|
)
|
|
@@ -191,7 +191,7 @@ def push_messages(
|
|
|
191
191
|
run_id = msg.metadata.run_id
|
|
192
192
|
|
|
193
193
|
# Record incoming traffic size
|
|
194
|
-
bytes_recv =
|
|
194
|
+
bytes_recv = request.ByteSize()
|
|
195
195
|
|
|
196
196
|
# Abort if the run is not running
|
|
197
197
|
abort_msg = check_abort(
|
|
@@ -218,7 +218,7 @@ def push_messages(
|
|
|
218
218
|
)
|
|
219
219
|
|
|
220
220
|
# Record outgoing traffic size
|
|
221
|
-
bytes_sent =
|
|
221
|
+
bytes_sent = response.ByteSize()
|
|
222
222
|
|
|
223
223
|
# Record traffic only if message was successfully processed
|
|
224
224
|
# Only one message is processed per request
|
|
@@ -165,20 +165,51 @@ class SqlLinkState(LinkState, SqlCoreState): # pylint: disable=R0904
|
|
|
165
165
|
|
|
166
166
|
return message.metadata.message_id
|
|
167
167
|
|
|
168
|
+
# pylint: disable-next=too-many-locals
|
|
168
169
|
def _check_stored_messages(self, message_ids: set[str]) -> None:
|
|
169
170
|
"""Check and delete the message if it's invalid."""
|
|
170
171
|
if not message_ids:
|
|
171
172
|
return
|
|
172
173
|
|
|
173
174
|
with self.session():
|
|
175
|
+
# Batch fetch all messages in one query
|
|
176
|
+
placeholders = ",".join([f":mid_{i}" for i in range(len(message_ids))])
|
|
177
|
+
query = f"""
|
|
178
|
+
SELECT * FROM message_ins
|
|
179
|
+
WHERE message_id IN ({placeholders})
|
|
180
|
+
"""
|
|
181
|
+
params = {f"mid_{i}": str(mid) for i, mid in enumerate(message_ids)}
|
|
182
|
+
message_rows = self.query(query, params)
|
|
183
|
+
|
|
184
|
+
if not message_rows:
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
# Build message lookup dict
|
|
188
|
+
message_dict: dict[str, dict[str, Any]] = {
|
|
189
|
+
row["message_id"]: row for row in message_rows
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# Collect unique run_ids for batch federation lookup
|
|
193
|
+
run_ids = {row["run_id"] for row in message_rows}
|
|
194
|
+
placeholders = ",".join([f":rid_{i}" for i in range(len(run_ids))])
|
|
195
|
+
query = f"""
|
|
196
|
+
SELECT run_id, federation FROM run
|
|
197
|
+
WHERE run_id IN ({placeholders})
|
|
198
|
+
"""
|
|
199
|
+
params = {f"rid_{i}": rid for i, rid in enumerate(run_ids)}
|
|
200
|
+
run_rows = self.query(query, params)
|
|
201
|
+
|
|
202
|
+
# Build run_id to federation mapping
|
|
203
|
+
run_id_to_federation: dict[int, str] = {
|
|
204
|
+
row["run_id"]: row["federation"] for row in run_rows
|
|
205
|
+
}
|
|
206
|
+
|
|
174
207
|
invalid_msg_ids: set[str] = set()
|
|
175
208
|
current_time = now().timestamp()
|
|
176
209
|
|
|
210
|
+
# Check each message for validity
|
|
177
211
|
for msg_id in message_ids:
|
|
178
|
-
|
|
179
|
-
query = "SELECT * FROM message_ins WHERE message_id = :message_id"
|
|
180
|
-
message_rows = self.query(query, {"message_id": msg_id})
|
|
181
|
-
message_row = message_rows[0] if message_rows else None
|
|
212
|
+
message_row = message_dict.get(msg_id)
|
|
182
213
|
if not message_row:
|
|
183
214
|
continue
|
|
184
215
|
|
|
@@ -188,15 +219,12 @@ class SqlLinkState(LinkState, SqlCoreState): # pylint: disable=R0904
|
|
|
188
219
|
invalid_msg_ids.add(msg_id)
|
|
189
220
|
continue
|
|
190
221
|
|
|
191
|
-
# Check if
|
|
192
|
-
# Get federation from run table
|
|
222
|
+
# Check if run exists and get federation
|
|
193
223
|
run_id = message_row["run_id"]
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if not run_row: # This should not happen
|
|
224
|
+
federation = run_id_to_federation.get(run_id)
|
|
225
|
+
if not federation:
|
|
197
226
|
invalid_msg_ids.add(msg_id)
|
|
198
227
|
continue
|
|
199
|
-
federation = run_row["federation"]
|
|
200
228
|
|
|
201
229
|
# Convert sint64 to uint64 for node IDs
|
|
202
230
|
src_node_id = int64_to_uint64(message_row["src_node_id"])
|
|
@@ -29,6 +29,8 @@ from flwr.cli.config_utils import get_fab_metadata
|
|
|
29
29
|
from flwr.common import Context, RecordDict, now
|
|
30
30
|
from flwr.common.constant import (
|
|
31
31
|
FAB_MAX_SIZE,
|
|
32
|
+
FEDERATION_NOT_FOUND_MESSAGE,
|
|
33
|
+
FEDERATION_NOT_SPECIFIED_MESSAGE,
|
|
32
34
|
HEARTBEAT_DEFAULT_INTERVAL,
|
|
33
35
|
LOG_STREAM_INTERVAL,
|
|
34
36
|
NO_ACCOUNT_AUTH_MESSAGE,
|
|
@@ -80,7 +82,7 @@ from flwr.proto.control_pb2 import ( # pylint: disable=E0611
|
|
|
80
82
|
from flwr.proto.federation_pb2 import Federation # pylint: disable=E0611
|
|
81
83
|
from flwr.proto.node_pb2 import NodeInfo # pylint: disable=E0611
|
|
82
84
|
from flwr.server.superlink.linkstate import LinkState, LinkStateFactory
|
|
83
|
-
from flwr.supercore.constant import PLATFORM_API_URL
|
|
85
|
+
from flwr.supercore.constant import NOOP_FEDERATION, PLATFORM_API_URL
|
|
84
86
|
from flwr.supercore.ffs import FfsFactory
|
|
85
87
|
from flwr.supercore.object_store import ObjectStore, ObjectStoreFactory
|
|
86
88
|
from flwr.supercore.primitives.asymmetric import bytes_to_public_key, uses_nist_ec_curve
|
|
@@ -149,10 +151,11 @@ class ControlServicer(control_pb2_grpc.ControlServicer):
|
|
|
149
151
|
)
|
|
150
152
|
|
|
151
153
|
# Check (1) federation exists and (2) the flwr_aid is a member
|
|
152
|
-
federation = request.federation
|
|
153
|
-
|
|
154
|
+
federation = request.federation or NOOP_FEDERATION
|
|
154
155
|
if not state.federation_manager.exists(federation):
|
|
155
|
-
|
|
156
|
+
if request.federation:
|
|
157
|
+
raise ValueError(FEDERATION_NOT_FOUND_MESSAGE % federation)
|
|
158
|
+
raise ValueError(FEDERATION_NOT_SPECIFIED_MESSAGE)
|
|
156
159
|
|
|
157
160
|
if not state.federation_manager.has_member(flwr_aid, federation):
|
|
158
161
|
raise ValueError(
|
|
@@ -170,7 +173,7 @@ class ControlServicer(control_pb2_grpc.ControlServicer):
|
|
|
170
173
|
fab_hash = ffs.put(fab.content, fab.verifications)
|
|
171
174
|
|
|
172
175
|
if fab_hash != fab.hash_str:
|
|
173
|
-
raise
|
|
176
|
+
raise ValueError(
|
|
174
177
|
f"FAB ({fab.hash_str}) hash from request doesn't match contents"
|
|
175
178
|
)
|
|
176
179
|
fab_id, fab_version = get_fab_metadata(fab.content)
|
|
@@ -180,7 +183,7 @@ class ControlServicer(control_pb2_grpc.ControlServicer):
|
|
|
180
183
|
fab_version,
|
|
181
184
|
fab_hash,
|
|
182
185
|
override_config,
|
|
183
|
-
|
|
186
|
+
federation,
|
|
184
187
|
federation_options,
|
|
185
188
|
flwr_aid,
|
|
186
189
|
)
|
|
@@ -206,13 +209,9 @@ class ControlServicer(control_pb2_grpc.ControlServicer):
|
|
|
206
209
|
# Register the context at the LinkState
|
|
207
210
|
state.set_serverapp_context(run_id=run_id, context=context)
|
|
208
211
|
|
|
209
|
-
|
|
210
|
-
except Exception as e:
|
|
212
|
+
except ValueError as e:
|
|
211
213
|
log(ERROR, "Could not start run: %s", str(e))
|
|
212
|
-
context.abort(
|
|
213
|
-
grpc.StatusCode.FAILED_PRECONDITION,
|
|
214
|
-
str(e),
|
|
215
|
-
)
|
|
214
|
+
context.abort(grpc.StatusCode.FAILED_PRECONDITION, str(e))
|
|
216
215
|
|
|
217
216
|
log(INFO, "Created run %s", str(run_id))
|
|
218
217
|
return StartRunResponse(run_id=run_id)
|
{flwr_nightly-1.26.0.dev20260123.dist-info → flwr_nightly-1.26.0.dev20260126.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: flwr-nightly
|
|
3
|
-
Version: 1.26.0.
|
|
3
|
+
Version: 1.26.0.dev20260126
|
|
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
|
{flwr_nightly-1.26.0.dev20260123.dist-info → flwr_nightly-1.26.0.dev20260126.dist-info}/RECORD
RENAMED
|
@@ -8,41 +8,41 @@ flwr/app/user_config.py,sha256=7xsOVs-NYIvE710iE1qRFqxlA3LQNnmcoVXqcg_Ez2g,802
|
|
|
8
8
|
flwr/cli/__init__.py,sha256=EfMGmHoobET6P2blBt_eOByXL8299MgFfB7XNdaPQ6I,720
|
|
9
9
|
flwr/cli/app.py,sha256=62ELnHyEbgTp2GDzgsuk-mJ09_Gs8WHysbRceOnH-x8,3851
|
|
10
10
|
flwr/cli/app_cmd/__init__.py,sha256=O_vTzS2u-n27fBYnDbjTMMKQQ2Bz80Y4JqGBHSAnK40,856
|
|
11
|
-
flwr/cli/app_cmd/publish.py,sha256=
|
|
12
|
-
flwr/cli/app_cmd/review.py,sha256=
|
|
11
|
+
flwr/cli/app_cmd/publish.py,sha256=DAFRu12EAO1NtzrGN9uYN_hW20gQZ2vCyf0HggzqaZ0,8006
|
|
12
|
+
flwr/cli/app_cmd/review.py,sha256=FPusshF36519z_KN36mU2fcvRtb5DCh0esinsts8WEs,7200
|
|
13
13
|
flwr/cli/auth_plugin/__init__.py,sha256=qpebWV9uLpx72_J8yTVgT1DlH2Y9MK_RraDoTYEqv-w,1359
|
|
14
14
|
flwr/cli/auth_plugin/auth_plugin.py,sha256=CKGsk0RvSmKPDfz5RsQIE_w1bWROBSOoVydbub4Z-I8,2813
|
|
15
15
|
flwr/cli/auth_plugin/noop_auth_plugin.py,sha256=GKrZ97w7NoZGn2r6gzqtMvyeHXeIkNKCLTfT_NHaiwg,3209
|
|
16
|
-
flwr/cli/auth_plugin/oidc_cli_plugin.py,sha256=
|
|
17
|
-
flwr/cli/build.py,sha256=
|
|
16
|
+
flwr/cli/auth_plugin/oidc_cli_plugin.py,sha256=1gN00FifSrhUA5s5uaE_GpQ3N7U8fQec4PXlSJr50RU,5975
|
|
17
|
+
flwr/cli/build.py,sha256=wjLrxWHGnwRFp7WxZFgm8J0cwcdQY9-QHxjFnEa2dII,10097
|
|
18
18
|
flwr/cli/cli_account_auth_interceptor.py,sha256=mXgxThpZjU_2Xlae9xT8ewOw60GeE64comDd57asLIY,3680
|
|
19
19
|
flwr/cli/config/__init__.py,sha256=46z6whA3hvKkl9APRs-UG7Ym3K9VOqKx_pYcgelRjtE,788
|
|
20
|
-
flwr/cli/config/ls.py,sha256=
|
|
20
|
+
flwr/cli/config/ls.py,sha256=llQiXC0kK7shT96Y3GD2uvVP3MjWWLpYTeExUH1rALo,3537
|
|
21
21
|
flwr/cli/config_migration.py,sha256=UM3lZU34TOhGXkjBBChyGaE6r7uaVV2NpyP4TZhFPyY,10848
|
|
22
|
-
flwr/cli/config_utils.py,sha256=
|
|
22
|
+
flwr/cli/config_utils.py,sha256=t9GNUPnUH1nUNZQ3oGjx6Jd5VSO9SlnQXjz3-yro7Rs,7683
|
|
23
23
|
flwr/cli/constant.py,sha256=Ihww6e-6V_Ocv7pRnIQeezdy72_RBL9dPYv87Yf4ppI,3612
|
|
24
24
|
flwr/cli/example.py,sha256=SNTorkKPrx1rOryGREUyZu8TcOc1-vFv1zEddaysdY0,2216
|
|
25
25
|
flwr/cli/federation/__init__.py,sha256=okxswL4fAjApI9gV_alU1lRkTUcQRbwlzvtUTLz61fE,793
|
|
26
|
-
flwr/cli/federation/ls.py,sha256=
|
|
27
|
-
flwr/cli/flower_config.py,sha256=
|
|
28
|
-
flwr/cli/install.py,sha256=
|
|
29
|
-
flwr/cli/log.py,sha256=
|
|
26
|
+
flwr/cli/federation/ls.py,sha256=xjS3HHNwOXXsaNN-xrdWvClmV29R6yT8R2AA0SVzJas,10689
|
|
27
|
+
flwr/cli/flower_config.py,sha256=8XGB9-XjJrpR2xYOcFWjmvmqGZRS5_M-rPhuZQQASSs,15267
|
|
28
|
+
flwr/cli/install.py,sha256=gBYg1SczpvBh8MMrXJxi0q4DUdzXCdvapK3jC3CBlPk,9114
|
|
29
|
+
flwr/cli/log.py,sha256=BPA0dvGlXx5PrtURPua5fJyF7iVrb28K4fEY2Uth0EE,7317
|
|
30
30
|
flwr/cli/login/__init__.py,sha256=B1SXKU3HCQhWfFDMJhlC7FOl8UsvH4mxysxeBnrfyUE,800
|
|
31
|
-
flwr/cli/login/login.py,sha256=
|
|
32
|
-
flwr/cli/ls.py,sha256=
|
|
31
|
+
flwr/cli/login/login.py,sha256=uUUrt6TNJjV_udwS3qJ5TXcRqqtI9nBySdXzwXItoVc,3999
|
|
32
|
+
flwr/cli/ls.py,sha256=ROSDyaQw5y9BjcBUtj57Vc3HgHTW_JbYX5NDIl-Yfhg,12793
|
|
33
33
|
flwr/cli/new/__init__.py,sha256=QA1E2QtzPvFCjLTUHnFnJbufuFiGyT_0Y53Wpbvg1F0,790
|
|
34
|
-
flwr/cli/new/new.py,sha256=
|
|
35
|
-
flwr/cli/pull.py,sha256=
|
|
34
|
+
flwr/cli/new/new.py,sha256=15phs5dhEpo7ORs7uulGzHWtW9W5ZGn0EKFE_T5WXb8,7829
|
|
35
|
+
flwr/cli/pull.py,sha256=XzBvLrdYGrSCuky5OoCEU4V9UR9QlyZMT7Tj-W6wSw0,2946
|
|
36
36
|
flwr/cli/run/__init__.py,sha256=RPyB7KbYTFl6YRiilCch6oezxrLQrl1kijV7BMGkLbA,790
|
|
37
|
-
flwr/cli/run/run.py,sha256=
|
|
37
|
+
flwr/cli/run/run.py,sha256=6yrVljd1CqqALq-wqiv0TnZlveiN5ymiUI5iTjuvUJM,8954
|
|
38
38
|
flwr/cli/run_utils.py,sha256=ZnlBOqFfRxVEPZKR_9zYrynNcohiCFY8QN6OFyOCrQs,5082
|
|
39
|
-
flwr/cli/stop.py,sha256=
|
|
39
|
+
flwr/cli/stop.py,sha256=w3frNt3TD5AukGmlGq4CbWo-3PC3B-YSLO6PjJeHjH4,4692
|
|
40
40
|
flwr/cli/supernode/__init__.py,sha256=DBkjoPo2hS2Y-ghJxwLbrAbCQFBgD82_Itl2_892UBo,917
|
|
41
41
|
flwr/cli/supernode/ls.py,sha256=x4ghJHpqcpDGuZx91ZehzSdmrUJ8ZuZvpY_UybnP63U,8224
|
|
42
|
-
flwr/cli/supernode/register.py,sha256=
|
|
43
|
-
flwr/cli/supernode/unregister.py,sha256=
|
|
42
|
+
flwr/cli/supernode/register.py,sha256=YPf28ZDvqPAQAt6c6OG73AJa9zZPONKgpxhnnb3NWQk,5498
|
|
43
|
+
flwr/cli/supernode/unregister.py,sha256=qWGg8G_KqF300ze4D1dYaSIRJZkl9zwM2ul8VLYjJMY,3719
|
|
44
44
|
flwr/cli/typing.py,sha256=MaY3NAca2PgmNByogksDKSCoRQLQpXTgO8NO9nLP0yA,8008
|
|
45
|
-
flwr/cli/utils.py,sha256=
|
|
45
|
+
flwr/cli/utils.py,sha256=jLhe-Voj0v8cK16MwRXW7qf34lEffOauRIukDyYX2II,15258
|
|
46
46
|
flwr/client/__init__.py,sha256=xwkPJfdeWxIIfmiPE5vnmnY_JbTlErP0Qs9eBP6qRFg,1252
|
|
47
47
|
flwr/client/client.py,sha256=3HAchxvknKG9jYbB7swNyDj-e5vUWDuMKoLvbT7jCVM,7895
|
|
48
48
|
flwr/client/dpfedavg_numpy_client.py,sha256=ELDHyEJcTB-FlLhHC-JXy8HuB3ZFHfT0HL3g1VSWY5w,7451
|
|
@@ -76,8 +76,8 @@ flwr/clientapp/typing.py,sha256=Ld2rsD40SvIRD1Z_IdAGaYSbRtkgXZ5CPFIkJJ3Ao-A,862
|
|
|
76
76
|
flwr/clientapp/utils.py,sha256=UEb3DFpAsAaG1ARU34D3L8njn4k1iPg-qpHl_Hyo8zA,4340
|
|
77
77
|
flwr/common/__init__.py,sha256=7Rf7Ti78N7vUydpIZ2b-1im6ciWz0G5_w91N9YEPsLM,4195
|
|
78
78
|
flwr/common/args.py,sha256=1ehgieKUQ9AtNcu2S5y501jB_mJ8mPA7GXiS3imGdgM,5799
|
|
79
|
-
flwr/common/config.py,sha256=
|
|
80
|
-
flwr/common/constant.py,sha256=
|
|
79
|
+
flwr/common/config.py,sha256=Y9cmrKOZUge3Dxg9ddlkYRI_o1wLAkXwiMsDbtdQkFA,13807
|
|
80
|
+
flwr/common/constant.py,sha256=CJiJxxmrW2ixencNokEv9XXAbVOJdirBT3UUl4pwZyQ,10153
|
|
81
81
|
flwr/common/context.py,sha256=pnFVcn-X0XKK1giw82I54meNodJJEoUwZ-NpkzT37zo,2421
|
|
82
82
|
flwr/common/differential_privacy.py,sha256=HiW2qQa5mCQfE0oePapFp3q0uZzjoHKthp5UrkRFtY8,6143
|
|
83
83
|
flwr/common/differential_privacy_constants.py,sha256=ruEjH4qF_S2bgxRI6brWCGWQPxFk-P7pviFguy9KvQ0,1074
|
|
@@ -260,7 +260,7 @@ flwr/server/superlink/fleet/grpc_rere/__init__.py,sha256=ahDJJ1e-lDxBpeBMgPk7YZt
|
|
|
260
260
|
flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=DtHuDP7fvgO-iamI48ACr0TQcD20eBYwMicHKIjhmXQ,12583
|
|
261
261
|
flwr/server/superlink/fleet/grpc_rere/node_auth_server_interceptor.py,sha256=88e9yEapAbk8fOoqG4f4-jvXMLuJAZYQF9j6whczegc,5811
|
|
262
262
|
flwr/server/superlink/fleet/message_handler/__init__.py,sha256=fHsRV0KvJ8HtgSA4_YBsEzuhJLjO8p6xx4aCY2oE1p4,731
|
|
263
|
-
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=
|
|
263
|
+
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=FO-CuOKfOwomjefmZsn_NGUM4m0ptPGwMOtfQTYfro0,12109
|
|
264
264
|
flwr/server/superlink/fleet/rest_rere/__init__.py,sha256=Lzc93nA7tDqoy-zRUaPG316oqFiZX1HUCL5ELaXY_xw,735
|
|
265
265
|
flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=cDWDQBf79F9tZ1mXOErc-nsnY4zFP9Tcv7Fs-bQIdiU,9616
|
|
266
266
|
flwr/server/superlink/fleet/vce/__init__.py,sha256=XOKbAWOzlCqEOQ3M2cBYkH7HKA7PxlbCJMunt-ty-DY,784
|
|
@@ -272,7 +272,7 @@ flwr/server/superlink/linkstate/__init__.py,sha256=KSUCjOfJlW1g6GAjlqK3mTviqLvJL
|
|
|
272
272
|
flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=1-aaSwad77SeYND3HEF2041KNG5ZA-8i1RcheYZMNq8,31689
|
|
273
273
|
flwr/server/superlink/linkstate/linkstate.py,sha256=Zz2vBI0QFFnptCUIeNjqQCbpCvMMzfsZluprEMkkHio,16809
|
|
274
274
|
flwr/server/superlink/linkstate/linkstate_factory.py,sha256=BTzPIqrWa797YKdOEHt0iu4ujxRcK4FNvUgjsOYQfMs,2859
|
|
275
|
-
flwr/server/superlink/linkstate/sql_linkstate.py,sha256=
|
|
275
|
+
flwr/server/superlink/linkstate/sql_linkstate.py,sha256=ziZvpEQBVV9GSxjHiS0fAyaY1G7x4y6XKlNLH-c_4TQ,48727
|
|
276
276
|
flwr/server/superlink/linkstate/utils.py,sha256=IA1mKKhGVBPoD61VXKFa8dZ_prnfuyWIuKAeeHPLmuE,16000
|
|
277
277
|
flwr/server/superlink/serverappio/__init__.py,sha256=Fy4zJuoccZe5mZSEIpOmQvU6YeXFBa1M4eZuXXmJcn8,717
|
|
278
278
|
flwr/server/superlink/serverappio/serverappio_grpc.py,sha256=2NFPynJMpYpT9C98Fr4n0QrTTjWBWbeUzlHcc6pg2kY,2279
|
|
@@ -387,7 +387,7 @@ flwr/superlink/servicer/control/control_account_auth_interceptor.py,sha256=AJs7G
|
|
|
387
387
|
flwr/superlink/servicer/control/control_event_log_interceptor.py,sha256=r9LjF5VkrE1sZIcml-sO2uOPAwpvr6dj4tFchqoDcR4,5965
|
|
388
388
|
flwr/superlink/servicer/control/control_grpc.py,sha256=AD7q2eo7RvXEhntcfVLgoKAR7AWfD_Z56Qw51wGLanY,4218
|
|
389
389
|
flwr/superlink/servicer/control/control_license_interceptor.py,sha256=8d28soJ0mLLhpSGCpuY2YzgbX0vVO7Sqe2DcApEgILc,3336
|
|
390
|
-
flwr/superlink/servicer/control/control_servicer.py,sha256
|
|
390
|
+
flwr/superlink/servicer/control/control_servicer.py,sha256=JUrnM2SrQeKM65_thKya_40_VoSdC1ml8zmxNhoXWb4,25529
|
|
391
391
|
flwr/supernode/__init__.py,sha256=KgeCaVvXWrU3rptNR1y0oBp4YtXbAcrnCcJAiOoWkI4,707
|
|
392
392
|
flwr/supernode/cli/__init__.py,sha256=JuEMr0-s9zv-PEWKuLB9tj1ocNfroSyNJ-oyv7ati9A,887
|
|
393
393
|
flwr/supernode/cli/flower_supernode.py,sha256=1HdhlGaVo51_7w3AL1hE5zfIVJMow0Ngi_F3Js01djs,10815
|
|
@@ -402,7 +402,7 @@ flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca
|
|
|
402
402
|
flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
|
|
403
403
|
flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=rRL4CQ0L78jF_p0ct4-JMGREt6wWRy__wy4czF4f54Y,11639
|
|
404
404
|
flwr/supernode/start_client_internal.py,sha256=BYk69UBQ2gQJaDQxXhccUgfOWrb7ShAstrbcMOCZIIs,26173
|
|
405
|
-
flwr_nightly-1.26.0.
|
|
406
|
-
flwr_nightly-1.26.0.
|
|
407
|
-
flwr_nightly-1.26.0.
|
|
408
|
-
flwr_nightly-1.26.0.
|
|
405
|
+
flwr_nightly-1.26.0.dev20260126.dist-info/METADATA,sha256=tcxTPuQjyJ_6Uyt2AjjiDY18bTLl4a5awalqhBFiJkM,14398
|
|
406
|
+
flwr_nightly-1.26.0.dev20260126.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
407
|
+
flwr_nightly-1.26.0.dev20260126.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
|
|
408
|
+
flwr_nightly-1.26.0.dev20260126.dist-info/RECORD,,
|
{flwr_nightly-1.26.0.dev20260123.dist-info → flwr_nightly-1.26.0.dev20260126.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|