Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
bf255df
change webui design - use alpine.js and tailwindcss instead of bootstrap
mhmdk0 Feb 9, 2026
20f89b2
return entity ID after aggregator and training experiment registration
mhmdk0 Feb 13, 2026
621981c
remove aggregator association - add set aggregator functionality
mhmdk0 Mar 13, 2026
b1ec682
allow multiple tasks to be ran and stopped (tasks to be ran in contai…
mhmdk0 Mar 13, 2026
5afc9d6
fix training submission to return the new entity id (to be used in th…
mhmdk0 Mar 13, 2026
bdc2487
fix ui logs - run tasks in interactive mode
mhmdk0 Mar 13, 2026
1805d5b
fix events error - that already exists (logs)
mhmdk0 Mar 13, 2026
32f9b6a
allow multiple tasks to be run in webui
mhmdk0 Mar 13, 2026
6e2e9ff
add support for training in webui routes
mhmdk0 Mar 13, 2026
8fa5dca
add training in webui templates and static
mhmdk0 Mar 13, 2026
6fba8b5
fix UI issues - JS refactoring according to the new flow
mhmdk0 Mar 13, 2026
6e9fae7
change task names in routes according to the new design.
mhmdk0 Mar 13, 2026
b918746
fix task logs upon changing profiles - reinitialization
mhmdk0 Mar 13, 2026
7c6d799
Merge remote-tracking branch 'origin/main' into webui-new-design
mhmdk0 Mar 14, 2026
9f70ef3
modify new added features to match the new design
mhmdk0 Mar 14, 2026
6513d5d
fix code scanning errors
mhmdk0 Mar 14, 2026
07090b8
fix cli unit tests - training tests
mhmdk0 Mar 14, 2026
530e815
fix modal not showing up caused by race condition
mhmdk0 Mar 15, 2026
87ff29a
add attributes for tests
mhmdk0 Mar 15, 2026
bc00c36
fix e2e tests
mhmdk0 Mar 15, 2026
c7f0e84
fix lint error
mhmdk0 Mar 15, 2026
5f2683f
remove remaining aggregator-association logic
hasan7n Mar 15, 2026
0ba4838
edit confusing message
hasan7n Mar 15, 2026
e9c3c2e
use update_training_exp for setting aggregator
hasan7n Mar 15, 2026
22284eb
use proper serializer
hasan7n Mar 15, 2026
13cd7a1
keep aggregator_association app minimally, add delete migration
hasan7n Mar 15, 2026
29dddff
fix interactive prints
mhmdk0 Mar 15, 2026
4528e4c
fix email overflow in user dropdown
mhmdk0 Mar 15, 2026
bb4e3c4
enhance login prints
mhmdk0 Mar 15, 2026
39d4872
Merge remote-tracking branch 'mhmdk0/webui-new-design' into webui-new…
mhmdk0 Mar 15, 2026
0fad806
Merge 'origin/main' into webui-new-design - update webui changes design
mhmdk0 Mar 17, 2026
b78005c
update webui according to new changes
mhmdk0 Mar 17, 2026
e48600c
fix data mismatch webui - left during testing
mhmdk0 Mar 17, 2026
fc903a7
Merge branch 'main' into webui-new-design
mhmdk0 Mar 17, 2026
3df9bd9
remove login warning about terminal from webui
hasan7n Mar 18, 2026
d89336b
fix mentioning compatiblity tests when there is no tests
hasan7n Mar 18, 2026
c2913eb
better ui/ux for cc input, fix js logic for cc forms
hasan7n Mar 18, 2026
d137ce3
make sync policy disappear if configure cc is toggled off
hasan7n Mar 18, 2026
d86eafd
fix sync policy button
hasan7n Mar 18, 2026
b78ea60
make it visible that users synced cc policy
hasan7n Mar 18, 2026
f151c95
fix dataset.is_prepared
hasan7n Mar 19, 2026
f47eb40
updatesubmit_as_prepared to new web design
hasan7n Mar 19, 2026
c2d7a58
remove dataset hash check from dataset dashboard
hasan7n Mar 19, 2026
be58432
check hashes before configuring CC
hasan7n Mar 19, 2026
760841b
store data in bucket as a last step
hasan7n Mar 19, 2026
c3cab65
refactor benchmark models
hasan7n Mar 19, 2026
1871f70
don't show reference model if it's a CC benchmark
hasan7n Mar 19, 2026
31c254d
remove unused css
hasan7n Mar 19, 2026
5734855
refactor cc configuration to leave long operations to the end
hasan7n Mar 20, 2026
7f7e63c
scroll to bottom when showing panel
hasan7n Mar 20, 2026
48d9161
use interactive mode for policy sync
hasan7n Mar 20, 2026
76f868c
update cc policy webui: show stages
hasan7n Mar 20, 2026
13c7ce6
allow users to delete certs anyway
hasan7n Mar 20, 2026
4a2be03
update medperf client version
hasan7n Mar 21, 2026
9445e02
add progress bar to large file gcp upload
hasan7n Mar 22, 2026
7ea8a3c
quick fix for javascript caching in browsers
hasan7n Mar 22, 2026
c1d4468
remove Medperf title
hasan7n Mar 22, 2026
9a0f70e
use "You" instead of entity owner ID when user is owner
hasan7n Mar 22, 2026
8c4ec31
use proper prism dark mode
hasan7n Mar 22, 2026
ff83482
update docs
hasan7n Mar 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 3 additions & 13 deletions cli/cli_tests_training.sh
Original file line number Diff line number Diff line change
Expand Up @@ -163,16 +163,6 @@ echo "AGG_UID=$AGG_UID" >> "$LAST_ENV_FILE"

echo "\n"

##########################################################
echo "====================================="
echo "Running aggregator association step"
echo "====================================="
print_eval medperf aggregator associate -a $AGG_UID -t $TRAINING_UID -y
checkFailed "aggregator association step failed"
##########################################################

echo "\n"

##########################################################
echo "====================================="
echo "Activate modelowner profile"
Expand All @@ -185,10 +175,10 @@ echo "\n"

##########################################################
echo "====================================="
echo "Approve aggregator association"
echo "Running set aggregator step"
echo "====================================="
print_eval medperf association approve -t $TRAINING_UID -a $AGG_UID
checkFailed "agg association approval failed"
print_eval medperf training set_aggregator -t $TRAINING_UID -a $AGG_UID -y
checkFailed "Setting aggregator failed"
##########################################################

echo "\n"
Expand Down
2 changes: 1 addition & 1 deletion cli/medperf/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.2.0"
__version__ = "0.3.0"
49 changes: 23 additions & 26 deletions cli/medperf/asset_management/asset_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from medperf.asset_management.asset_storage_manager import AssetStorageManager
from medperf.asset_management.asset_policy_manager import AssetPolicyManager
from medperf.asset_management.cc_operator import OperatorManager
from medperf.utils import tar, generate_tmp_path
from medperf.utils import tar, generate_tmp_path, remove_path
import secrets
from medperf.exceptions import MedperfException
from medperf import config as medperf_config
Expand Down Expand Up @@ -42,55 +42,52 @@ def setup_dataset_for_cc(dataset: Dataset):
cc_policy = dataset.get_cc_policy()
__verify_cloud_environment(cc_config)

# create dataset asset
# policy setup
medperf_config.ui.text = "Generating encryption key"
encryption_key = generate_encryption_key()
asset_policy_manager = AssetPolicyManager(cc_config)
asset_policy_manager.setup_policy(cc_policy, encryption_key)

# storage
medperf_config.ui.text = "Compressing dataset"
asset_path = generate_tmp_path()
tar(asset_path, [dataset.data_path, dataset.labels_path])

__setup_asset_for_cc(cc_config, cc_policy, asset_path)
asset_storage_manager = AssetStorageManager(cc_config, asset_path, encryption_key)
asset_storage_manager.store_asset()
del encryption_key
remove_path(asset_path)


def setup_model_for_cc(model: Model):
if not model.is_cc_configured():
return
cc_config = model.get_cc_config()
cc_policy = model.get_cc_policy()
if model.type != "ASSET":
if not model.is_asset():
raise MedperfException(
f"Model {model.id} is not a file-based asset and cannot be set up for confidential computing."
)
asset = model.asset_obj
# create model asset
asset_path = asset.get_archive_path()

__verify_cloud_environment(cc_config)
__setup_asset_for_cc(cc_config, cc_policy, asset_path, for_model=True)


def __verify_cloud_environment(cc_config: dict):
AssetStorageManager(cc_config, None, None).setup()


def __setup_asset_for_cc(
cc_config: dict,
cc_policy: dict,
asset_path: str,
for_model: bool = False,
):
# create encryption key
# policy setup
medperf_config.ui.text = "Generating encryption key"
encryption_key = generate_encryption_key()

asset_storage_manager = AssetStorageManager(cc_config, asset_path, encryption_key)
asset_policy_manager = AssetPolicyManager(cc_config, for_model=for_model)
asset_policy_manager = AssetPolicyManager(cc_config, for_model=True)
asset_policy_manager.setup_policy(cc_policy, encryption_key)

# storage
asset_storage_manager = AssetStorageManager(cc_config, asset_path, encryption_key)
asset_storage_manager.store_asset()

# policy setup
asset_policy_manager.setup_policy(cc_policy, encryption_key)
del encryption_key


def __verify_cloud_environment(cc_config: dict):
AssetStorageManager(cc_config, None, None).setup()


def update_dataset_cc_policy(dataset: Dataset, permitted_workloads: list[CCWorkloadID]):
if not dataset.is_cc_configured():
raise MedperfException(
Expand All @@ -108,7 +105,7 @@ def update_model_cc_policy(model: Model, permitted_workloads: list[CCWorkloadID]
f"Model {model.id} does not have a configuration for confidential computing."
)
cc_config = model.get_cc_config()
if model.type != "ASSET":
if not model.is_asset():
raise MedperfException(
f"Model {model.id} is not a file-based asset and cannot be set up for confidential computing."
)
Expand Down
32 changes: 25 additions & 7 deletions cli/medperf/asset_management/asset_storage_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
remove_path,
)
from medperf.encryption import SymmetricEncryption
from medperf.asset_management.gcp_utils import GCPAssetConfig, upload_file_to_gcs
from medperf.asset_management.gcp_utils import (
GCPAssetConfig,
upload_from_file_object_to_gcs,
)
from medperf.asset_management.asset_check import verify_asset_owner_setup
from medperf.asset_management.utils import CustomWriter, get_file_size
from medperf.exceptions import MedperfException
from medperf import config as medperf_config
from tqdm import tqdm


class AssetStorageManager:
Expand All @@ -30,12 +35,25 @@ def __encrypt_asset(self):
asset_hash = get_file_hash(tmp_encrypted_asset_path)
return tmp_encrypted_asset_path, asset_hash

def __upload_encrypted_asset(self, tmp_encrypted_asset_path):
upload_file_to_gcs(
self.config,
tmp_encrypted_asset_path,
self.config.encrypted_asset_bucket_file,
)
def __upload_encrypted_asset(self, tmp_encrypted_asset_path: str):
with open(tmp_encrypted_asset_path, "rb") as in_file:
with tqdm.wrapattr(
in_file,
"read",
total=get_file_size(in_file),
miniters=1,
desc="Uploading encrypted dataset to the bucket",
unit="B",
unit_scale=True,
unit_divisor=1024,
file=CustomWriter(),
) as file_obj:
upload_from_file_object_to_gcs(
self.config,
file_obj,
self.config.encrypted_asset_bucket_file,
)
remove_path(tmp_encrypted_asset_path)

def setup(self):
medperf_config.ui.text = "Verifying Cloud Environment"
Expand Down
2 changes: 2 additions & 0 deletions cli/medperf/asset_management/gcp_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .kms import set_kms_iam_policy, encrypt_with_kms_key
from .storage import (
upload_file_to_gcs,
upload_from_file_object_to_gcs,
upload_string_to_gcs,
download_file_from_gcs,
download_string_from_gcs,
Expand All @@ -19,6 +20,7 @@
"set_kms_iam_policy",
"encrypt_with_kms_key",
"upload_file_to_gcs",
"upload_from_file_object_to_gcs",
"upload_string_to_gcs",
"download_file_from_gcs",
"download_string_from_gcs",
Expand Down
10 changes: 10 additions & 0 deletions cli/medperf/asset_management/gcp_utils/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ def upload_file_to_gcs(
blob.upload_from_filename(local_file)


def upload_from_file_object_to_gcs(
config: Union[GCPAssetConfig, GCPOperatorConfig], file: object, gcs_path: str
):
"""Upload file to Google Cloud Storage."""
client = storage.Client()
bucket = client.bucket(config.bucket)
blob = bucket.blob(gcs_path)
blob.upload_from_file(file)


def upload_string_to_gcs(
config: Union[GCPAssetConfig, GCPOperatorConfig], content: bytes, gcs_path: str
):
Expand Down
21 changes: 21 additions & 0 deletions cli/medperf/asset_management/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from medperf import config
import os


class CustomWriter:
"""class to use with tqdm to print progress using config.ui"""

def write(self, msg):
config.ui.print(msg)

def flush(self):
pass


def get_file_size(file_object) -> int:
"""Get the size of a file in bytes."""
try:
total_bytes = os.fstat(file_object.fileno()).st_size
except (AttributeError, OSError):
total_bytes = None
return total_bytes
17 changes: 0 additions & 17 deletions cli/medperf/commands/aggregator/aggregator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import medperf.config as config
from medperf.decorators import clean_except
from medperf.commands.aggregator.submit import SubmitAggregator
from medperf.commands.aggregator.associate import AssociateAggregator
from medperf.commands.aggregator.run import StartAggregator

from medperf.commands.list import EntityList
Expand Down Expand Up @@ -33,22 +32,6 @@ def submit(
config.ui.print("✅ Done!")


@app.command("associate")
@clean_except
def associate(
aggregator_id: int = typer.Option(
..., "--aggregator_id", "-a", help="UID of benchmark to associate with"
),
training_exp_id: int = typer.Option(
..., "--training_exp_id", "-t", help="UID of benchmark to associate with"
),
approval: bool = typer.Option(False, "-y", help="Skip approval step"),
):
"""Associates an aggregator with a training experiment."""
AssociateAggregator.run(aggregator_id, training_exp_id, approved=approval)
config.ui.print("✅ Done!")


@app.command("start")
@clean_except
def run(
Expand Down
14 changes: 7 additions & 7 deletions cli/medperf/commands/aggregator/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ def run(
training_exp_id (int): Training experiment UID.
"""
execution = cls(training_exp_id, publish_on, overwrite)
execution.prepare()
execution.validate()
execution.check_existing_outputs()
execution.prepare_aggregator()
execution.prepare_participants_list()
execution.prepare_plan()
execution.prepare_pki_assets()
with config.ui.interactive():
execution.prepare()
execution.validate()
execution.check_existing_outputs()
execution.prepare_aggregator()
execution.prepare_participants_list()
execution.prepare_plan()
execution.prepare_pki_assets()
execution.run_experiment()

def __init__(self, training_exp_id, publish_on, overwrite) -> None:
Expand Down
5 changes: 3 additions & 2 deletions cli/medperf/commands/aggregator/submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def run(cls, name: str, address: str, port: int, aggregation_mlcube: int):
updated_benchmark_body = submission.submit()
ui.print("Uploaded")
submission.write(updated_benchmark_body)
return submission.aggregator.id

def __init__(self, name: str, address: str, port: int, aggregation_mlcube: int):
self.ui = config.ui
Expand All @@ -41,5 +42,5 @@ def submit(self):

def write(self, updated_body):
remove_path(self.aggregator.path)
aggregator = Aggregator(**updated_body)
aggregator.write()
self.aggregator = Aggregator(**updated_body)
self.aggregator.write()
21 changes: 7 additions & 14 deletions cli/medperf/commands/association/approval.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ def run(
training_exp_uid: int = None,
dataset_uid: int = None,
model_uid: int = None,
aggregator_uid: int = None,
):
"""Sets approval status for an association between a benchmark and a dataset or mlcube
"""Sets approval status for an association between a benchmark and a dataset or mlcube,
or between a training experiment and a dataset.

Args:
benchmark_uid (int): Benchmark UID.
approval_status (str): Desired approval status to set for the association.
comms (Comms): Instance of Comms interface.
ui (UI): Instance of UI interface.
training_exp_uid (int, optional): Training experiment UID. Defaults to None.
dataset_uid (int, optional): Dataset UID. Defaults to None.
mlcube_uid (int, optional): MLCube UID. Defaults to None.
"""
Expand All @@ -28,7 +27,6 @@ def run(
training_exp_uid,
dataset_uid,
model_uid,
aggregator_uid,
approval_status.value,
)
update = {"approval_status": approval_status.value}
Expand All @@ -42,12 +40,7 @@ def run(
comms.update_benchmark_model_association(
benchmark_uid, model_uid, update
)
if training_exp_uid:
if dataset_uid:
comms.update_training_dataset_association(
training_exp_uid, dataset_uid, update
)
if aggregator_uid:
comms.update_training_aggregator_association(
training_exp_uid, aggregator_uid, update
)
if training_exp_uid and dataset_uid:
comms.update_training_dataset_association(
training_exp_uid, dataset_uid, update
)
Loading
Loading