flwr-nightly 1.22.0.dev20250917__py3-none-any.whl → 1.22.0.dev20250919__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 (33) hide show
  1. flwr/cli/new/new.py +2 -0
  2. flwr/cli/new/templates/app/code/client.xgboost.py.tpl +110 -0
  3. flwr/cli/new/templates/app/code/server.xgboost.py.tpl +56 -0
  4. flwr/cli/new/templates/app/code/task.xgboost.py.tpl +67 -0
  5. flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +61 -0
  6. flwr/clientapp/mod/__init__.py +2 -1
  7. flwr/clientapp/mod/centraldp_mods.py +155 -39
  8. flwr/clientapp/typing.py +22 -0
  9. flwr/common/constant.py +1 -0
  10. flwr/common/exit/exit_code.py +4 -0
  11. flwr/common/record/typeddict.py +12 -0
  12. flwr/serverapp/strategy/__init__.py +12 -0
  13. flwr/serverapp/strategy/dp_adaptive_clipping.py +335 -0
  14. flwr/serverapp/strategy/dp_fixed_clipping.py +71 -49
  15. flwr/serverapp/strategy/fedadagrad.py +0 -3
  16. flwr/serverapp/strategy/fedadam.py +0 -3
  17. flwr/serverapp/strategy/fedavgm.py +3 -3
  18. flwr/serverapp/strategy/fedprox.py +1 -1
  19. flwr/serverapp/strategy/fedtrimmedavg.py +1 -1
  20. flwr/serverapp/strategy/fedxgb_cyclic.py +220 -0
  21. flwr/serverapp/strategy/fedyogi.py +0 -3
  22. flwr/serverapp/strategy/krum.py +230 -0
  23. flwr/serverapp/strategy/qfedavg.py +252 -0
  24. flwr/supercore/cli/flower_superexec.py +26 -1
  25. flwr/supercore/constant.py +19 -0
  26. flwr/supercore/superexec/plugin/exec_plugin.py +11 -1
  27. flwr/supercore/superexec/run_superexec.py +16 -2
  28. {flwr_nightly-1.22.0.dev20250917.dist-info → flwr_nightly-1.22.0.dev20250919.dist-info}/METADATA +1 -1
  29. {flwr_nightly-1.22.0.dev20250917.dist-info → flwr_nightly-1.22.0.dev20250919.dist-info}/RECORD +31 -23
  30. flwr/serverapp/dp_fixed_clipping.py +0 -352
  31. flwr/serverapp/strategy/strategy_utils_tests.py +0 -323
  32. {flwr_nightly-1.22.0.dev20250917.dist-info → flwr_nightly-1.22.0.dev20250919.dist-info}/WHEEL +0 -0
  33. {flwr_nightly-1.22.0.dev20250917.dist-info → flwr_nightly-1.22.0.dev20250919.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,252 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Fair Resource Allocation in Federated Learning [Li et al., 2020] strategy.
16
+
17
+ Paper: openreview.net/pdf?id=ByexElSYDr
18
+ """
19
+
20
+
21
+ from collections import OrderedDict
22
+ from collections.abc import Iterable
23
+ from logging import INFO
24
+ from typing import Callable, Optional
25
+
26
+ import numpy as np
27
+
28
+ from flwr.common import (
29
+ Array,
30
+ ArrayRecord,
31
+ ConfigRecord,
32
+ Message,
33
+ MetricRecord,
34
+ NDArray,
35
+ RecordDict,
36
+ )
37
+ from flwr.common.logger import log
38
+ from flwr.server import Grid
39
+
40
+ from ..exception import AggregationError
41
+ from .fedavg import FedAvg
42
+
43
+
44
+ class QFedAvg(FedAvg):
45
+ """Q-FedAvg strategy.
46
+
47
+ Implementation based on openreview.net/pdf?id=ByexElSYDr
48
+
49
+ Parameters
50
+ ----------
51
+ client_learning_rate : float
52
+ Local learning rate used by clients during training. This value is used by
53
+ the strategy to approximate the base Lipschitz constant L, via
54
+ L = 1 / client_learning_rate.
55
+ q : float (default: 0.1)
56
+ The parameter q that controls the degree of fairness of the algorithm. Please
57
+ tune this parameter based on your use case.
58
+ When set to 0, q-FedAvg is equivalent to FedAvg.
59
+ train_loss_key : str (default: "train_loss")
60
+ The key within the MetricRecord whose value is used as the training loss when
61
+ aggregating ArrayRecords following q-FedAvg.
62
+ fraction_train : float (default: 1.0)
63
+ Fraction of nodes used during training. In case `min_train_nodes`
64
+ is larger than `fraction_train * total_connected_nodes`, `min_train_nodes`
65
+ will still be sampled.
66
+ fraction_evaluate : float (default: 1.0)
67
+ Fraction of nodes used during validation. In case `min_evaluate_nodes`
68
+ is larger than `fraction_evaluate * total_connected_nodes`,
69
+ `min_evaluate_nodes` will still be sampled.
70
+ min_train_nodes : int (default: 2)
71
+ Minimum number of nodes used during training.
72
+ min_evaluate_nodes : int (default: 2)
73
+ Minimum number of nodes used during validation.
74
+ min_available_nodes : int (default: 2)
75
+ Minimum number of total nodes in the system.
76
+ weighted_by_key : str (default: "num-examples")
77
+ The key within each MetricRecord whose value is used as the weight when
78
+ computing weighted averages for MetricRecords.
79
+ arrayrecord_key : str (default: "arrays")
80
+ Key used to store the ArrayRecord when constructing Messages.
81
+ configrecord_key : str (default: "config")
82
+ Key used to store the ConfigRecord when constructing Messages.
83
+ train_metrics_aggr_fn : Optional[callable] (default: None)
84
+ Function with signature (list[RecordDict], str) -> MetricRecord,
85
+ used to aggregate MetricRecords from training round replies.
86
+ If `None`, defaults to `aggregate_metricrecords`, which performs a weighted
87
+ average using the provided weight factor key.
88
+ evaluate_metrics_aggr_fn : Optional[callable] (default: None)
89
+ Function with signature (list[RecordDict], str) -> MetricRecord,
90
+ used to aggregate MetricRecords from training round replies.
91
+ If `None`, defaults to `aggregate_metricrecords`, which performs a weighted
92
+ average using the provided weight factor key.
93
+ """
94
+
95
+ def __init__( # pylint: disable=R0913, R0917
96
+ self,
97
+ client_learning_rate: float,
98
+ q: float = 0.1,
99
+ train_loss_key: str = "train_loss",
100
+ fraction_train: float = 1.0,
101
+ fraction_evaluate: float = 1.0,
102
+ min_train_nodes: int = 2,
103
+ min_evaluate_nodes: int = 2,
104
+ min_available_nodes: int = 2,
105
+ weighted_by_key: str = "num-examples",
106
+ arrayrecord_key: str = "arrays",
107
+ configrecord_key: str = "config",
108
+ train_metrics_aggr_fn: Optional[
109
+ Callable[[list[RecordDict], str], MetricRecord]
110
+ ] = None,
111
+ evaluate_metrics_aggr_fn: Optional[
112
+ Callable[[list[RecordDict], str], MetricRecord]
113
+ ] = None,
114
+ ) -> None:
115
+ super().__init__(
116
+ fraction_train=fraction_train,
117
+ fraction_evaluate=fraction_evaluate,
118
+ min_train_nodes=min_train_nodes,
119
+ min_evaluate_nodes=min_evaluate_nodes,
120
+ min_available_nodes=min_available_nodes,
121
+ weighted_by_key=weighted_by_key,
122
+ arrayrecord_key=arrayrecord_key,
123
+ configrecord_key=configrecord_key,
124
+ train_metrics_aggr_fn=train_metrics_aggr_fn,
125
+ evaluate_metrics_aggr_fn=evaluate_metrics_aggr_fn,
126
+ )
127
+ self.q = q
128
+ self.client_learning_rate = client_learning_rate
129
+ self.train_loss_key = train_loss_key
130
+ self.current_arrays: Optional[ArrayRecord] = None
131
+
132
+ def summary(self) -> None:
133
+ """Log summary configuration of the strategy."""
134
+ log(INFO, "\t├──> q-FedAvg settings:")
135
+ log(INFO, "\t│\t├── client_learning_rate: %s", self.client_learning_rate)
136
+ log(INFO, "\t│\t├── q: %s", self.q)
137
+ log(INFO, "\t│\t└── train_loss_key: '%s'", self.train_loss_key)
138
+ super().summary()
139
+
140
+ def configure_train(
141
+ self, server_round: int, arrays: ArrayRecord, config: ConfigRecord, grid: Grid
142
+ ) -> Iterable[Message]:
143
+ """Configure the next round of federated training."""
144
+ self.current_arrays = arrays.copy()
145
+ return super().configure_train(server_round, arrays, config, grid)
146
+
147
+ def aggregate_train( # pylint: disable=too-many-locals
148
+ self,
149
+ server_round: int,
150
+ replies: Iterable[Message],
151
+ ) -> tuple[Optional[ArrayRecord], Optional[MetricRecord]]:
152
+ """Aggregate ArrayRecords and MetricRecords in the received Messages."""
153
+ # Call FedAvg aggregate_train to perform validation and aggregation
154
+ valid_replies, _ = self._check_and_log_replies(replies, is_train=True)
155
+
156
+ if not valid_replies:
157
+ return None, None
158
+
159
+ # Compute estimate of Lipschitz constant L
160
+ L = 1.0 / self.client_learning_rate # pylint: disable=C0103
161
+
162
+ # q-FedAvg aggregation
163
+ if self.current_arrays is None:
164
+ raise AggregationError(
165
+ "Current global model weights are not available. Make sure to call"
166
+ "`configure_train` before calling `aggregate_train`."
167
+ )
168
+ array_keys = list(self.current_arrays.keys()) # Preserve keys
169
+ global_weights = self.current_arrays.to_numpy_ndarrays(keep_input=False)
170
+ sum_delta = None
171
+ sum_h = 0.0
172
+
173
+ for msg in valid_replies:
174
+ # Extract local weights and training loss from Message
175
+ local_weights = get_local_weights(msg)
176
+ loss = get_train_loss(msg, self.train_loss_key)
177
+
178
+ # Compute delta and h
179
+ delta, h = compute_delta_and_h(
180
+ global_weights, local_weights, self.q, L, loss
181
+ )
182
+
183
+ # Compute sum of deltas and sum of h
184
+ if sum_delta is None:
185
+ sum_delta = delta
186
+ else:
187
+ sum_delta = [sd + d for sd, d in zip(sum_delta, delta)]
188
+ sum_h += h
189
+
190
+ # Compute new global weights and convert to Array type
191
+ # `np.asarray` can convert numpy scalars to 0-dim arrays
192
+ assert sum_delta is not None # Make mypy happy
193
+ array_list = [
194
+ Array(np.asarray(gw - (d / sum_h)))
195
+ for gw, d in zip(global_weights, sum_delta)
196
+ ]
197
+
198
+ # Aggregate MetricRecords
199
+ metrics = self.train_metrics_aggr_fn(
200
+ [msg.content for msg in valid_replies],
201
+ self.weighted_by_key,
202
+ )
203
+ return ArrayRecord(OrderedDict(zip(array_keys, array_list))), metrics
204
+
205
+
206
+ def get_train_loss(msg: Message, loss_key: str) -> float:
207
+ """Extract training loss from a Message."""
208
+ metrics = list(msg.content.metric_records.values())[0]
209
+ if (loss := metrics.get(loss_key)) is None or not isinstance(loss, (int, float)):
210
+ raise AggregationError(
211
+ "Missing or invalid training loss. "
212
+ f"The strategy expected a float value for the key '{loss_key}' "
213
+ "as the training loss in each MetricRecord from the clients. "
214
+ f"Ensure that '{loss_key}' is present and maps to a valid float."
215
+ )
216
+ return float(loss)
217
+
218
+
219
+ def get_local_weights(msg: Message) -> list[NDArray]:
220
+ """Extract local weights from a Message."""
221
+ arrays = list(msg.content.array_records.values())[0]
222
+ return arrays.to_numpy_ndarrays(keep_input=False)
223
+
224
+
225
+ def l2_norm(ndarrays: list[NDArray]) -> float:
226
+ """Compute the squared L2 norm of a list of numpy.ndarray."""
227
+ return float(sum(np.sum(np.square(g)) for g in ndarrays))
228
+
229
+
230
+ def compute_delta_and_h(
231
+ global_weights: list[NDArray],
232
+ local_weights: list[NDArray],
233
+ q: float,
234
+ L: float, # Lipschitz constant # pylint: disable=C0103
235
+ loss: float,
236
+ ) -> tuple[list[NDArray], float]:
237
+ """Compute delta and h used in q-FedAvg aggregation."""
238
+ # Compute gradient_k = L * (w - w_k)
239
+ for gw, lw in zip(global_weights, local_weights):
240
+ np.subtract(gw, lw, out=lw)
241
+ lw *= L
242
+ grad = local_weights # After in-place operations, local_weights is now grad
243
+ # Compute ||w_k - w||^2
244
+ norm = l2_norm(grad)
245
+ # Compute delta_k = loss_k^q * gradient_k
246
+ loss_pow_q: float = np.float_power(loss + 1e-10, q)
247
+ for g in grad:
248
+ g *= loss_pow_q
249
+ delta = grad # After in-place multiplication, grad is now delta
250
+ # Compute h_k
251
+ h = q * np.float_power(loss + 1e-10, q - 1) * norm + L * loss_pow_q
252
+ return delta, h
@@ -17,7 +17,9 @@
17
17
 
18
18
  import argparse
19
19
  from logging import INFO
20
- from typing import Optional
20
+ from typing import Any, Optional
21
+
22
+ import yaml
21
23
 
22
24
  from flwr.common import EventType, event
23
25
  from flwr.common.constant import ExecPluginType
@@ -26,6 +28,7 @@ from flwr.common.logger import log
26
28
  from flwr.proto.clientappio_pb2_grpc import ClientAppIoStub
27
29
  from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub
28
30
  from flwr.proto.simulationio_pb2_grpc import SimulationIoStub
31
+ from flwr.supercore.constant import EXEC_PLUGIN_SECTION
29
32
  from flwr.supercore.grpc_health import add_args_health
30
33
  from flwr.supercore.superexec.plugin import (
31
34
  ClientAppExecPlugin,
@@ -36,6 +39,7 @@ from flwr.supercore.superexec.plugin import (
36
39
  from flwr.supercore.superexec.run_superexec import run_superexec
37
40
 
38
41
  try:
42
+ from flwr.ee import add_ee_args_superexec
39
43
  from flwr.ee.constant import ExecEePluginType
40
44
  from flwr.ee.exec_plugin import get_ee_plugin_and_stub_class
41
45
  except ImportError:
@@ -54,6 +58,10 @@ except ImportError:
54
58
  """Get the EE plugin class and stub class based on the plugin type."""
55
59
  return None
56
60
 
61
+ # pylint: disable-next=unused-argument
62
+ def add_ee_args_superexec(parser: argparse.ArgumentParser) -> None:
63
+ """Add EE-specific arguments to the parser."""
64
+
57
65
 
58
66
  def flower_superexec() -> None:
59
67
  """Run `flower-superexec` command."""
@@ -70,12 +78,28 @@ def flower_superexec() -> None:
70
78
  # Trigger telemetry event
71
79
  event(EventType.RUN_SUPEREXEC_ENTER, {"plugin_type": args.plugin_type})
72
80
 
81
+ # Load plugin config from YAML file if provided
82
+ plugin_config = None
83
+ if plugin_config_path := getattr(args, "plugin_config", None):
84
+ try:
85
+ with open(plugin_config_path, encoding="utf-8") as file:
86
+ yaml_config: Optional[dict[str, Any]] = yaml.safe_load(file)
87
+ if yaml_config is None or EXEC_PLUGIN_SECTION not in yaml_config:
88
+ raise ValueError(f"Missing '{EXEC_PLUGIN_SECTION}' section.")
89
+ plugin_config = yaml_config[EXEC_PLUGIN_SECTION]
90
+ except (FileNotFoundError, yaml.YAMLError, ValueError) as e:
91
+ flwr_exit(
92
+ ExitCode.SUPEREXEC_INVALID_PLUGIN_CONFIG,
93
+ f"Failed to load plugin config from '{plugin_config_path}': {e!r}",
94
+ )
95
+
73
96
  # Get the plugin class and stub class based on the plugin type
74
97
  plugin_class, stub_class = _get_plugin_and_stub_class(args.plugin_type)
75
98
  run_superexec(
76
99
  plugin_class=plugin_class,
77
100
  stub_class=stub_class, # type: ignore
78
101
  appio_api_address=args.appio_api_address,
102
+ plugin_config=plugin_config,
79
103
  flwr_dir=args.flwr_dir,
80
104
  parent_pid=args.parent_pid,
81
105
  health_server_address=args.health_server_address,
@@ -122,6 +146,7 @@ def _parse_args() -> argparse.ArgumentParser:
122
146
  help="The PID of the parent process. When set, the process will terminate "
123
147
  "when the parent process exits.",
124
148
  )
149
+ add_ee_args_superexec(parser)
125
150
  add_args_health(parser)
126
151
  return parser
127
152
 
@@ -0,0 +1,19 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Constants for Flower infrastructure."""
16
+
17
+
18
+ # Top-level key in YAML config for exec plugin settings
19
+ EXEC_PLUGIN_SECTION = "exec_plugin"
@@ -17,7 +17,7 @@
17
17
 
18
18
  from abc import ABC, abstractmethod
19
19
  from collections.abc import Sequence
20
- from typing import Callable, Optional
20
+ from typing import Any, Callable, Optional
21
21
 
22
22
  from flwr.common.typing import Run
23
23
 
@@ -69,3 +69,13 @@ class ExecPlugin(ABC):
69
69
  The ID of the run associated with the token, used for tracking or
70
70
  logging purposes.
71
71
  """
72
+
73
+ # This method is optional to implement
74
+ def load_config(self, yaml_config: dict[str, Any]) -> None:
75
+ """Load configuration from a YAML dictionary.
76
+
77
+ Parameters
78
+ ----------
79
+ yaml_config : dict[str, Any]
80
+ A dictionary representing the YAML configuration.
81
+ """
@@ -17,10 +17,10 @@
17
17
 
18
18
  import time
19
19
  from logging import WARN
20
- from typing import Optional, Union
20
+ from typing import Any, Optional, Union
21
21
 
22
22
  from flwr.common.config import get_flwr_dir
23
- from flwr.common.exit import register_signal_handlers
23
+ from flwr.common.exit import ExitCode, flwr_exit, register_signal_handlers
24
24
  from flwr.common.grpc import create_channel, on_channel_state_change
25
25
  from flwr.common.logger import log
26
26
  from flwr.common.retry_invoker import _make_simple_grpc_retry_invoker, _wrap_stub
@@ -47,6 +47,7 @@ def run_superexec( # pylint: disable=R0913,R0914,R0917
47
47
  type[ClientAppIoStub], type[ServerAppIoStub], type[SimulationIoStub]
48
48
  ],
49
49
  appio_api_address: str,
50
+ plugin_config: Optional[dict[str, Any]] = None,
50
51
  flwr_dir: Optional[str] = None,
51
52
  parent_pid: Optional[int] = None,
52
53
  health_server_address: Optional[str] = None,
@@ -61,6 +62,9 @@ def run_superexec( # pylint: disable=R0913,R0914,R0917
61
62
  The gRPC stub class for the AppIO API.
62
63
  appio_api_address : str
63
64
  The address of the AppIO API.
65
+ plugin_config : Optional[dict[str, Any]] (default: None)
66
+ The configuration dictionary for the plugin. If `None`, the plugin will use
67
+ its default configuration.
64
68
  flwr_dir : Optional[str] (default: None)
65
69
  The Flower directory.
66
70
  parent_pid : Optional[int] (default: None)
@@ -113,6 +117,16 @@ def run_superexec( # pylint: disable=R0913,R0914,R0917
113
117
  get_run=get_run,
114
118
  )
115
119
 
120
+ # Load plugin configuration from file if provided
121
+ try:
122
+ if plugin_config is not None:
123
+ plugin.load_config(plugin_config)
124
+ except (KeyError, ValueError) as e:
125
+ flwr_exit(
126
+ code=ExitCode.SUPEREXEC_INVALID_PLUGIN_CONFIG,
127
+ message=f"Invalid plugin config: {e!r}",
128
+ )
129
+
116
130
  # Start the main loop
117
131
  try:
118
132
  while True:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: flwr-nightly
3
- Version: 1.22.0.dev20250917
3
+ Version: 1.22.0.dev20250919
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
@@ -18,7 +18,7 @@ flwr/cli/login/__init__.py,sha256=B1SXKU3HCQhWfFDMJhlC7FOl8UsvH4mxysxeBnrfyUE,80
18
18
  flwr/cli/login/login.py,sha256=RM1Jiv_VFm3oz4rTHSr3D87X90lW3WzErjBBU7WviWY,4309
19
19
  flwr/cli/ls.py,sha256=3YK7cpoImJ7PbjlP_JgYRQWz1GymX2q7Reu-mKJEpao,10957
20
20
  flwr/cli/new/__init__.py,sha256=QA1E2QtzPvFCjLTUHnFnJbufuFiGyT_0Y53Wpbvg1F0,790
21
- flwr/cli/new/new.py,sha256=GvXapfYMrUQMktn1qJ3bfxfXeK0IAsxsxHBIGfPg3sE,10535
21
+ flwr/cli/new/new.py,sha256=nIuUrQSGDjI4kqnymlq-rOT0RU3AHwZrat3abqHhCwM,10598
22
22
  flwr/cli/new/templates/__init__.py,sha256=FpjWCfIySU2DB4kh0HOXLAjlZNNFDTVU4w3HoE2TzcI,725
23
23
  flwr/cli/new/templates/app/.gitignore.tpl,sha256=HZJcGQoxp7aUzaPg8Uqch3kNrIESwr9yjimDxJYgXVY,3104
24
24
  flwr/cli/new/templates/app/LICENSE.tpl,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
@@ -39,6 +39,7 @@ flwr/cli/new/templates/app/code/client.pytorch.py.tpl,sha256=fYoh-dTu07LkqNYvwcx
39
39
  flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl,sha256=fuxVmZpjHIueNy_aHWF81531vmi8DGu4CYjYDqmUwWo,1705
40
40
  flwr/cli/new/templates/app/code/client.sklearn.py.tpl,sha256=0qqEe-RRjkHGOH8gsD9e83ae-kyyYixhyBgzVHjYpzk,3500
41
41
  flwr/cli/new/templates/app/code/client.tensorflow.py.tpl,sha256=8o55KXpsbF_rv6o98ZNYJDCazjwMp_RPTaSzDfT7Qlw,2682
42
+ flwr/cli/new/templates/app/code/client.xgboost.py.tpl,sha256=-ipRV8gqpbEg7Mht77Yyqs1viL-3JYSVZR47I7xeG4c,3493
42
43
  flwr/cli/new/templates/app/code/dataset.baseline.py.tpl,sha256=jbd_exHAk2-Blu_kVutjPO6a_dkJQWb232zxSeXIZ1k,1453
43
44
  flwr/cli/new/templates/app/code/flwr_tune/__init__.py,sha256=Xq5fEn5yZkw6HAJi10T_3HRBoqN5_5pNqJHY4wXvD5k,748
44
45
  flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl,sha256=p6BzTbP-mXkFANiVC7iz3YlskOidWaLC341IJyrUotQ,2951
@@ -56,6 +57,7 @@ flwr/cli/new/templates/app/code/server.pytorch.py.tpl,sha256=epARqfcQ-EQsdZwaaaU
56
57
  flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl,sha256=gvBsGA_Jg9kAH8xTxjzTjMcvBtciuccOwQFbO7ey8tU,916
57
58
  flwr/cli/new/templates/app/code/server.sklearn.py.tpl,sha256=ehQ5VRgBn92WeFl6kupwJnuxSNkKvE-EvKde6A9mNQo,1377
58
59
  flwr/cli/new/templates/app/code/server.tensorflow.py.tpl,sha256=2-WTOPd-ewdLd9QmSlflIH7ix7zxAzPEOZoyiPBOy8c,1010
60
+ flwr/cli/new/templates/app/code/server.xgboost.py.tpl,sha256=fwtCRyCG2hDSH1zVMyZv7zA7wsdKNPfpugDSZjxCs5Q,1746
59
61
  flwr/cli/new/templates/app/code/strategy.baseline.py.tpl,sha256=YkHAgppUeD2BnBoGfVB6dEvBfjuIPGsU1gw4CiUi3qA,40
60
62
  flwr/cli/new/templates/app/code/task.huggingface.py.tpl,sha256=piBbY3Dg60bQnCg15uzMw0QiL5SDOYX4YhQouy-X2zI,3164
61
63
  flwr/cli/new/templates/app/code/task.jax.py.tpl,sha256=Fb0XgdTAQplM-ZCusI081XA9asO3gHptH772S-Xcyy8,1525
@@ -65,6 +67,7 @@ flwr/cli/new/templates/app/code/task.pytorch.py.tpl,sha256=RKA5lV6O6OnVKZ2r75pbz
65
67
  flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl,sha256=XlJqA4Ix_PloO_zJLhjiN5vDj16w3I4CPVGdmbe8asE,3800
66
68
  flwr/cli/new/templates/app/code/task.sklearn.py.tpl,sha256=vHdhtMp0FHxbYafXyhDT9aKmmmA0Jvpx5Oum1Yu9lWY,1850
67
69
  flwr/cli/new/templates/app/code/task.tensorflow.py.tpl,sha256=impgWN7MfztmcWF4xh1llcZGsgTvrb1HD5ZE0t-8U08,1731
70
+ flwr/cli/new/templates/app/code/task.xgboost.py.tpl,sha256=0xO8jQvrHuB1llVDopQPOmt5Hn6rBw8umzoNwiZZs-o,2135
68
71
  flwr/cli/new/templates/app/code/utils.baseline.py.tpl,sha256=YkHAgppUeD2BnBoGfVB6dEvBfjuIPGsU1gw4CiUi3qA,40
69
72
  flwr/cli/new/templates/app/pyproject.baseline.toml.tpl,sha256=mPIMPfneVYV03l8jWRgWZ0V5Kh_pJw-AMUvkhcKkmL8,3182
70
73
  flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl,sha256=wqYW4bWcf12m0U2njR995lySSesFvnHB-eSkPWz-QdM,2501
@@ -76,6 +79,7 @@ flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=SE4H23OFkQbqNU64nYf
76
79
  flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl,sha256=docQbs3MuRR-yT24lVz7N2sQL3Sj49EHuOCuRj_0djQ,1508
77
80
  flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=apauU_PUmLEbt2rjckKniEbzdRs1EnMri_qgtHtBJZ8,1484
78
81
  flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=LQpDKJTEnRKj5Ygn5FkT44SxlnLVprkPlbrGaFf5Q50,1508
82
+ flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl,sha256=504pHibNRGFe-DLnzqHLYhKeF_n8BPMv0Xog5EfnZ0M,1661
79
83
  flwr/cli/pull.py,sha256=dHiMe6x8w8yRoFNKpjA-eiPD6eFiHz4Vah5HZrqNpuo,3364
80
84
  flwr/cli/run/__init__.py,sha256=RPyB7KbYTFl6YRiilCch6oezxrLQrl1kijV7BMGkLbA,790
81
85
  flwr/cli/run/run.py,sha256=ECa0kup9dn15O70H74QdgUsEaeErbzDqVX_U0zZO5IM,8173
@@ -109,15 +113,16 @@ flwr/client/rest_client/connection.py,sha256=fyiS1aXTv71jWczx7mSco94LYJTBXgTF-p2
109
113
  flwr/client/run_info_store.py,sha256=MaJ3UQ-07hWtK67wnWu0zR29jrk0fsfgJX506dvEOfE,4042
110
114
  flwr/client/typing.py,sha256=Jw3rawDzI_-ZDcRmEQcs5gZModY7oeQlEeltYsdOhlU,1048
111
115
  flwr/clientapp/__init__.py,sha256=uoTjvIynfGvMhsmc7iYK-5qJ0AdKKjCbx7WTKc0KeSk,828
112
- flwr/clientapp/mod/__init__.py,sha256=o8bbHMYb_aiPbGp6guRA-I_gCJaJQ6mCqSRZdfBYYrs,920
113
- flwr/clientapp/mod/centraldp_mods.py,sha256=M8vvdrjfLVsFLMd9JXqD_-P08q9jsNOgu_4AAs-X9Zk,4626
116
+ flwr/clientapp/mod/__init__.py,sha256=LZveV-U2YZVEH4FyAZMXMT-aD1Hc0I5miIkDr7p6uQQ,970
117
+ flwr/clientapp/mod/centraldp_mods.py,sha256=a-F-ELs3lt_wtmLl8900ExJiIY792cPCrmwmJKRrerI,8950
118
+ flwr/clientapp/typing.py,sha256=x1GvXWy112RqZh27liJqz-yZ7SSCOwiOSmAQsUxk9MY,853
114
119
  flwr/common/__init__.py,sha256=5GCLVk399Az_rTJHNticRlL0Sl_oPw_j5_LuFKfX7-M,4171
115
120
  flwr/common/address.py,sha256=9JucdTwlc-jpeJkRKeUboZoacUtErwSVtnDR9kAtLqE,4119
116
121
  flwr/common/args.py,sha256=Nq2u4yePbkSY0CWFamn0hZY6Rms8G1xYDeDGIcLIITE,5849
117
122
  flwr/common/auth_plugin/__init__.py,sha256=DktrRcGZrRarLf7Jb_UlHtOyLp9_-kEplyq6PS5-vOA,988
118
123
  flwr/common/auth_plugin/auth_plugin.py,sha256=mM7SuphO4OsVAVJR1GErYVgYT83ZjxDzS_gha12bT9E,4855
119
124
  flwr/common/config.py,sha256=glcZDjco-amw1YfQcYTFJ4S1pt9APoexT-mf1QscuHs,13960
120
- flwr/common/constant.py,sha256=zopMTlEgz8TxMSh59ueef6VutHSdfb3XRStl1W5yBZ4,9013
125
+ flwr/common/constant.py,sha256=y3yKgr1UxAWveUkw29z8KM2hKsZJqhHUqPPhKQeef80,9045
121
126
  flwr/common/context.py,sha256=Be8obQR_OvEDy1OmshuUKxGRQ7Qx89mf5F4xlhkR10s,2407
122
127
  flwr/common/date.py,sha256=1ZT2cRSpC2DJqprOVTLXYCR_O2_OZR0zXO_brJ3LqWc,1554
123
128
  flwr/common/differential_privacy.py,sha256=FdlpdpPl_H_2HJa8CQM1iCUGBBQ5Dc8CzxmHERM-EoE,6148
@@ -127,7 +132,7 @@ flwr/common/event_log_plugin/__init__.py,sha256=ts3VAL3Fk6Grp1EK_1Qg_V-BfOof9F86
127
132
  flwr/common/event_log_plugin/event_log_plugin.py,sha256=4SkVa1Ic-sPlICJShBuggXmXDcQtWQ1KDby4kthFNF0,2064
128
133
  flwr/common/exit/__init__.py,sha256=8W7xaO1iw0vacgmQW7FTFbSh7csNv6XfsgIlnIbNF6U,978
129
134
  flwr/common/exit/exit.py,sha256=DcXJfbpW1g-pQJqSZmps-1MZydd7T7RaarghIf2e4tU,3636
130
- flwr/common/exit/exit_code.py,sha256=e8O71zIqVT1H84mNBeenTz7S39yPZSpZQm-xUenpzN4,5249
135
+ flwr/common/exit/exit_code.py,sha256=Xa1NFGny2cefZ62kZZOfT8eii__PolMWCHxYmxoSQ2s,5416
131
136
  flwr/common/exit/exit_handler.py,sha256=uzDdWwhKgc1w5csZS52b86kjmEApmDZKwMn_X0zDZZo,2126
132
137
  flwr/common/exit/signal_handler.py,sha256=wqxykrwgmpFzmEMhpnlM7RtO0PnqIvYiSB1qYahZ5Sk,3710
133
138
  flwr/common/grpc.py,sha256=nHnFC7E84pZVTvd6BhcSYWnGd0jf8t5UmGea04qvilM,9806
@@ -148,7 +153,7 @@ flwr/common/record/configrecord.py,sha256=G7U0q39kB0Kyi0zMxFmPxcVemL9NgwVS1qjvI4
148
153
  flwr/common/record/conversion_utils.py,sha256=wbNCzy7oAqaA3-arhls_EqRZYXRC4YrWIoE-Gy82fJ0,1191
149
154
  flwr/common/record/metricrecord.py,sha256=KOyJjJbvFV6IwBPbgm92FZ_0_hXpMHuwfCi1rh5Zddk,8954
150
155
  flwr/common/record/recorddict.py,sha256=p7hBimFpKM1XKUe6OAkR_7pYGzGL_EwUJUvJ8odZEcY,14986
151
- flwr/common/record/typeddict.py,sha256=dDKgUThs2BscYUNcgP82KP8-qfAYXYftDrf2LszAC_o,3599
156
+ flwr/common/record/typeddict.py,sha256=NkHvzTEiicrLstryvTb-2O-sMvtsLgsOjAbostSf1z0,4102
152
157
  flwr/common/recorddict_compat.py,sha256=D5SqXWkqBddn5b6K_5UoH7aZ11UaN3lDTlzvHx3-rqk,14119
153
158
  flwr/common/retry_invoker.py,sha256=uQeDcgoTgmFwhJ0mkDE2eNz2acF9eShaqMOO5boGrPQ,15285
154
159
  flwr/common/secure_aggregation/__init__.py,sha256=MgW6uHGhyFLBAYQqa1Vzs5n2Gc0d4yEw1_NmerFir70,731
@@ -331,24 +336,26 @@ flwr/server/workflow/secure_aggregation/__init__.py,sha256=vGkycLb65CxdaMkKsANxQ
331
336
  flwr/server/workflow/secure_aggregation/secagg_workflow.py,sha256=b_pKk7gmbahwyj0ftOOLXvu-AMtRHEc82N9PJTEO8dc,5839
332
337
  flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=DkayCsnlAya6Y2PZsueLgoUCMRtV-GbnW08RfWx_SXM,29460
333
338
  flwr/serverapp/__init__.py,sha256=ZujKNXULwhWYQhFnxOOT5Wi9MRq2JCWFhAAj7ouiQ78,884
334
- flwr/serverapp/dp_fixed_clipping.py,sha256=wbP4W7CaUHXdll8ZSVUnTBSEWrnWM00CGk63rOR-Q2s,12133
335
339
  flwr/serverapp/exception.py,sha256=5cuH-2AafvihzosWDdDjuMmHdDqZ1XxHvCqZXNBVklw,1334
336
- flwr/serverapp/strategy/__init__.py,sha256=MJNWeBWzpQDfqhhpND5LncxPVK91kUo_Yzu6IMFdLCc,1492
337
- flwr/serverapp/strategy/dp_fixed_clipping.py,sha256=wbP4W7CaUHXdll8ZSVUnTBSEWrnWM00CGk63rOR-Q2s,12133
338
- flwr/serverapp/strategy/fedadagrad.py,sha256=fD65P6OEERa_pxq847e1UZpA083AcWR44XavYB0naGM,6343
339
- flwr/serverapp/strategy/fedadam.py,sha256=s3xPIqhopy6yPTeFxevSPnc7a6BcKnKsvo2AaO6Z_xs,7138
340
+ flwr/serverapp/strategy/__init__.py,sha256=QQFa0uMXWrSCTVbd7Ixk_48U6o3K-g4nLYYJUhEVbfo,1877
341
+ flwr/serverapp/strategy/dp_adaptive_clipping.py,sha256=mssiVGMgfJw8DeP6_pBSZUKWmaXvYeG-B-p7RSt2tAU,13600
342
+ flwr/serverapp/strategy/dp_fixed_clipping.py,sha256=C_faT0ggzeUB2bGv_r1Vss-fv7-UhDrpmfiHATESI0w,12832
343
+ flwr/serverapp/strategy/fedadagrad.py,sha256=faFsuKZziPTCLeNrJOyKbPTNo-1xrIZOz7SWT5rdjJs,6269
344
+ flwr/serverapp/strategy/fedadam.py,sha256=NsY_V6TGFAfCeA9vmqaLpvB_T5siJEtKozKGdxJssAI,7064
340
345
  flwr/serverapp/strategy/fedavg.py,sha256=Bq_nlmngzJbjqX1fF1mevXGVN6-pwglHv-6yNrs6lkA,12035
341
- flwr/serverapp/strategy/fedavgm.py,sha256=VlByltWzUYCoiVIWPFRqsqLKNWjlOlO2INK8SUxEjzk,8327
346
+ flwr/serverapp/strategy/fedavgm.py,sha256=FtFmBGLzuUQ_7JWk85Xh19d8sP0YDwqczGTliGzZyGs,8333
342
347
  flwr/serverapp/strategy/fedmedian.py,sha256=b31Dk0LQBbQxi_f-jeSbWHI7iOBugcuBSN2Az-_a75E,2596
343
348
  flwr/serverapp/strategy/fedopt.py,sha256=kqT0uV2IUE93O72XEVa1JJo61dcwbZEoT9KmYTjR2tE,8477
344
- flwr/serverapp/strategy/fedprox.py,sha256=XkkLTk3XpXAj0QoAzHqAvcAlPjrNlX11ISAu5u2x6X8,7026
345
- flwr/serverapp/strategy/fedtrimmedavg.py,sha256=4-QxgAQGo_7vB_L7qDYy28d95OBt9MeDa92yaTRMHqk,7166
349
+ flwr/serverapp/strategy/fedprox.py,sha256=J1KrcE5DFko6i4608iICv1G0t9MPXspjibPd-SF_HT8,7028
350
+ flwr/serverapp/strategy/fedtrimmedavg.py,sha256=58xDPc_YO41QM8jXn0gZ79PFzO8zo3Mh3UlkF0UBbIA,7168
346
351
  flwr/serverapp/strategy/fedxgb_bagging.py,sha256=ktDjzov4y0BRecioq788umCEtcuwElou9olBizQKOnM,3282
347
- flwr/serverapp/strategy/fedyogi.py,sha256=1Ripr4Hi2cdeTOLiFOXtMKvOxR3BsUQwc7bbTrXN4LM,6653
352
+ flwr/serverapp/strategy/fedxgb_cyclic.py,sha256=8H8WoLdG4Fy1_dtLLE4AYiidC-Cvaw2GxySfzAb7Xj0,8774
353
+ flwr/serverapp/strategy/fedyogi.py,sha256=Y9RFBQaNch3fPgGXF7OfnTH6eOpavZxpMWxWVIC9_SY,6579
354
+ flwr/serverapp/strategy/krum.py,sha256=iUM7MFCKQcSqUO3Eu4JKWnMc8NV0WMQW9dZXm4onQ-s,9490
355
+ flwr/serverapp/strategy/qfedavg.py,sha256=EM1tO_ovkybOBeW-h1PYX0lszCUAVHT6hUpwXykAEps,10204
348
356
  flwr/serverapp/strategy/result.py,sha256=E0Hl2VLnZAgQJjE2GDoKsK7JX-kPPU2KXc47Axt6hGw,4295
349
357
  flwr/serverapp/strategy/strategy.py,sha256=8uJGGm1ROLZERQ_dkRS7Z_rs-yK6XCE0UxXtIdFiEWk,10789
350
358
  flwr/serverapp/strategy/strategy_utils.py,sha256=hiwS7k-Hx6_c4NZXoKpHucS5CBKb7f8GppXRBSMt3Us,10851
351
- flwr/serverapp/strategy/strategy_utils_tests.py,sha256=_adS23Lrv1QA6V_3oZ7P_csMd8RqDObFeIhOkFnNtTg,10690
352
359
  flwr/simulation/__init__.py,sha256=Gg6OsP1Z-ixc3-xxzvl7j7rz2Fijy9rzyEPpxgAQCeM,1556
353
360
  flwr/simulation/app.py,sha256=b_bDyZFwBf2zpKs37Vmd5cFJSzDRE0fL-8uqA0UkAv4,10393
354
361
  flwr/simulation/legacy_app.py,sha256=nMISQqW0otJL1-2Kfd94O6BLlGS2IEmEPKTM2WGKrIs,15861
@@ -361,7 +368,8 @@ flwr/simulation/simulationio_connection.py,sha256=mzS1C6EEREwQDPceDo30anAasmTDLb
361
368
  flwr/supercore/__init__.py,sha256=pqkFoow_E6UhbBlhmoD1gmTH-33yJRhBsIZqxRPFZ7U,755
362
369
  flwr/supercore/app_utils.py,sha256=K76Zt6R670b1hUmxOsNc1WUCVYvF7lejXPcCO9K0Q0g,1753
363
370
  flwr/supercore/cli/__init__.py,sha256=EDl2aO-fuQfxSbL-T1W9RAfA2N0hpWHmqX_GSwblJbQ,845
364
- flwr/supercore/cli/flower_superexec.py,sha256=kov4uEeihf7QEUAfHEgdEvsL_8nL_fzQI9EePnRM1Ww,5012
371
+ flwr/supercore/cli/flower_superexec.py,sha256=JtqYrEWVu3BxLkjavsdohTOwvMwzuFqWP5j4Mo9dqsk,6155
372
+ flwr/supercore/constant.py,sha256=F9kRjisedaZcoyGvUITSDmIG12QDSCpo2LlM_l-q6jM,820
365
373
  flwr/supercore/corestate/__init__.py,sha256=Vau6-L_JG5QzNqtCTa9xCKGGljc09wY8avZmIjSJemg,774
366
374
  flwr/supercore/corestate/corestate.py,sha256=rDAWWeG5DcpCyQso9Z3RhwL4zr2IroPlRMcDzqoSu8s,2328
367
375
  flwr/supercore/ffs/__init__.py,sha256=U3KXwG_SplEvchat27K0LYPoPHzh-cwwT_NHsGlYMt8,908
@@ -382,10 +390,10 @@ flwr/supercore/superexec/__init__.py,sha256=XKX208hZ6a9gZ4KT9kMqfpCtp_8VGxekzKFf
382
390
  flwr/supercore/superexec/plugin/__init__.py,sha256=GNwq8uNdE8RB7ywEFRAvKjLFzgS3YXgz39-HBGsemWw,1035
383
391
  flwr/supercore/superexec/plugin/base_exec_plugin.py,sha256=fL-Ufc9Dp56OhWOzNSJUc7HumbkuSDYqZKwde2opG4g,2074
384
392
  flwr/supercore/superexec/plugin/clientapp_exec_plugin.py,sha256=9FT6ufEqV5K9g4FaAB9lVDbIv-VCH5LcxT4YKy23roE,1035
385
- flwr/supercore/superexec/plugin/exec_plugin.py,sha256=w3jmtxdv7ov_EdAgifKcm4q8nV39e2Xna4sNjqClwOM,2447
393
+ flwr/supercore/superexec/plugin/exec_plugin.py,sha256=4WtCQ4bsuFRlfCbg91ZcPAsX8htrCCo_fFh1DKo3cCQ,2764
386
394
  flwr/supercore/superexec/plugin/serverapp_exec_plugin.py,sha256=IwRzdPV-cSKwrP2krGh0De4IkAuxsmgK0WU6J-2GXqM,1035
387
395
  flwr/supercore/superexec/plugin/simulation_exec_plugin.py,sha256=upn5zE-YKkl_jTw8RzmeyQ58PU_UAlQ7CqnBXXdng8I,1060
388
- flwr/supercore/superexec/run_superexec.py,sha256=8hUlaVPVNnhePQ9OUgen4yy0fSGZAVggBGzm-33iJPw,6630
396
+ flwr/supercore/superexec/run_superexec.py,sha256=JiwKq9s_WPpk0S9MSi1lIgMZU120NOZLf4GlObHzI_k,7217
389
397
  flwr/supercore/utils.py,sha256=ebuHMbeA8eXisX0oMPqBK3hk7uVnIE_yiqWVz8YbkpQ,1324
390
398
  flwr/superlink/__init__.py,sha256=GNSuJ4-N6Z8wun2iZNlXqENt5beUyzC0Gi_tN396bbM,707
391
399
  flwr/superlink/artifact_provider/__init__.py,sha256=pgZEcVPKRE874LSu3cgy0HbwSJBIpVy_HxQOmne4PAs,810
@@ -411,7 +419,7 @@ flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca
411
419
  flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
412
420
  flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=nIHRu38EWK-rpNOkcgBRAAKwYQQWFeCwu0lkO7OPZGQ,10239
413
421
  flwr/supernode/start_client_internal.py,sha256=Y9S1-QlO2WP6eo4JvWzIpfaCoh2aoE7bjEYyxNNnlyg,20777
414
- flwr_nightly-1.22.0.dev20250917.dist-info/METADATA,sha256=NGqW090jvJZ7dg66AQQr9jKb4SF7DdF78xwqFuhtFUE,14559
415
- flwr_nightly-1.22.0.dev20250917.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
416
- flwr_nightly-1.22.0.dev20250917.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
417
- flwr_nightly-1.22.0.dev20250917.dist-info/RECORD,,
422
+ flwr_nightly-1.22.0.dev20250919.dist-info/METADATA,sha256=RuNCBGYrX4Df-vdIVKJ6Do1uDfeKomIIJ557ZlE8uLI,14559
423
+ flwr_nightly-1.22.0.dev20250919.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
424
+ flwr_nightly-1.22.0.dev20250919.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
425
+ flwr_nightly-1.22.0.dev20250919.dist-info/RECORD,,