Skip to content

Commit 4640cf0

Browse files
author
Douglas Blank
committed
[NA] Update for modern comet_ml and mlflow
1 parent a572702 commit 4640cf0

File tree

5 files changed

+141
-29
lines changed

5 files changed

+141
-29
lines changed

README.md

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ Upload prepared data to Comet.ml? [y/N] y
8888
# Start uploading data to Comet.ml
8989
100%|███████████████████████████████████████████████████████████████████████| 6/6 [01:00<00:00, 15s/it]
9090
Explore your experiment data on Comet.ml with the following links:
91-
- https://www.comet.ml/kstewart/mlflow-default-2bacc9?loginToken=NjKgD6f9ZuZWeudP76sDPHx9j
92-
- https://www.comet.ml/kstewart/mlflow-keras-experiment-2bacc9?loginToken=NjKgD6f9ZuZWeudP76sDPHx9j
93-
- https://www.comet.ml/kstewart/mlflow-tensorflow-keras-experiment-2bacc9?loginToken=NjKgD6f9ZuZWeudP76sDPHx9j
94-
Get deeper instrumentation by adding Comet SDK to your project: https://comet.ml/docs/python-sdk/mlflow/
91+
- https://www.comet.com/kstewart/mlflow-default-2bacc9?loginToken=NjKgD6f9ZuZWeudP76sDPHx9j
92+
- https://www.comet.com/kstewart/mlflow-keras-experiment-2bacc9?loginToken=NjKgD6f9ZuZWeudP76sDPHx9j
93+
- https://www.comet.com/kstewart/mlflow-tensorflow-keras-experiment-2bacc9?loginToken=NjKgD6f9ZuZWeudP76sDPHx9j
94+
Get deeper instrumentation by adding Comet SDK to your project: https://comet.com/docs/python-sdk/mlflow/
9595
9696
97-
If you need support, you can contact us at http://chat.comet.ml/ or https://comet.ml/docs/quick-start/#getting-support
97+
If you need support, you can contact us at http://chat.comet.com/ or https://comet.com/docs/quick-start/#getting-support
9898
```
9999

100100

@@ -122,6 +122,30 @@ Or with a MLFlow server:
122122
comet_for_mlflow --mlflow-store-uri http://localhost:5000
123123
```
124124

125+
### Authenticated MLflow stores (Databricks, etc.)
126+
127+
For authenticated MLflow stores like Databricks, you need to set authentication environment variables:
128+
129+
**For Databricks:**
130+
```bash
131+
export DATABRICKS_TOKEN=your_databricks_personal_access_token
132+
comet_for_mlflow --mlflow-store-uri https://your-workspace.cloud.databricks.com
133+
```
134+
135+
You can generate a Databricks personal access token by:
136+
1. Click on your user profile icon in the top-right corner of your Databricks workspace
137+
2. Select **User Settings**
138+
3. Go to the **Access Tokens** tab
139+
4. Click **Generate New Token**
140+
141+
**For other authenticated MLflow servers:**
142+
```bash
143+
# Username/password authentication
144+
export MLFLOW_TRACKING_USERNAME=your_username
145+
export MLFLOW_TRACKING_PASSWORD=your_password
146+
comet_for_mlflow --mlflow-store-uri https://your-mlflow-server.com
147+
```
148+
125149
## Importing MLFlow artifacts stored remotely
126150

127151
If your MLFlow runs have artifacts stored remotely (in any of supported remote artifact stores https://www.mlflow.org/docs/latest/tracking.html#artifact-stores), you need to configure your environment the same way as when you ran those experiments. For example, with a local Minio server:

comet_for_mlflow/comet_for_mlflow.py

Lines changed: 105 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@
3333
from os.path import abspath
3434
from zipfile import ZipFile
3535

36-
from comet_ml import API
37-
from comet_ml.comet import format_url
36+
from comet_ml import API, get_comet_api_client
3837
from comet_ml.config import get_api_key, get_config
39-
from comet_ml.connection import Reporting, get_comet_api_client, url_join
38+
from comet_ml.connection import Reporting
4039
from comet_ml.exceptions import CometRestApiException
4140
from comet_ml.offline import upload_single_offline_experiment
41+
from comet_ml.utils import merge_url, url_join
4242
from mlflow.entities.run_tag import RunTag
43+
from mlflow.exceptions import RestException
4344
from mlflow.tracking import _get_store
4445
from mlflow.tracking._model_registry.utils import _get_store as get_model_registry_store
4546
from mlflow.tracking.registry import UnsupportedModelRegistryStoreURIException
@@ -126,13 +127,70 @@ def __init__(
126127
output_dir = tempfile.mkdtemp()
127128

128129
# MLFlow conversion
129-
self.store = _get_store(mlflow_store_uri)
130+
try:
131+
self.store = _get_store(mlflow_store_uri)
132+
except RestException as e:
133+
# Check HTTP status code for authentication errors
134+
status_code = (
135+
e.get_http_status_code() if hasattr(e, "get_http_status_code") else None
136+
)
137+
error_msg = str(e)
138+
if (
139+
status_code == 401
140+
or "401" in error_msg
141+
or "Credential" in error_msg
142+
or "authentication" in error_msg.lower()
143+
):
144+
self._log_authentication_error(
145+
mlflow_store_uri, "connecting to MLflow store"
146+
)
147+
raise
148+
except Exception as e:
149+
error_msg = str(e)
150+
if (
151+
"401" in error_msg
152+
or "Credential" in error_msg
153+
or "authentication" in error_msg.lower()
154+
):
155+
self._log_authentication_error(
156+
mlflow_store_uri, "connecting to MLflow store"
157+
)
158+
raise
159+
130160
try:
131161
self.model_registry_store = get_model_registry_store(mlflow_store_uri)
132162
except UnsupportedModelRegistryStoreURIException:
133163
self.model_registry_store = None
134164

135-
self.mlflow_experiments = search_mlflow_store_experiments(self.store)
165+
try:
166+
self.mlflow_experiments = search_mlflow_store_experiments(self.store)
167+
except RestException as e:
168+
# Check HTTP status code for authentication errors
169+
status_code = (
170+
e.get_http_status_code() if hasattr(e, "get_http_status_code") else None
171+
)
172+
error_msg = str(e)
173+
if (
174+
status_code == 401
175+
or "401" in error_msg
176+
or "Credential" in error_msg
177+
or "authentication" in error_msg.lower()
178+
):
179+
self._log_authentication_error(
180+
mlflow_store_uri, "accessing MLflow experiments"
181+
)
182+
raise
183+
except Exception as e:
184+
error_msg = str(e)
185+
if (
186+
"401" in error_msg
187+
or "Credential" in error_msg
188+
or "authentication" in error_msg.lower()
189+
):
190+
self._log_authentication_error(
191+
mlflow_store_uri, "accessing MLflow experiments"
192+
)
193+
raise
136194
self.len_experiments = len(self.mlflow_experiments) # We start counting at 0
137195

138196
self.summary = {
@@ -206,7 +264,7 @@ def prepare(self):
206264
LOGGER.info(
207265
tabulate(
208266
table,
209-
headers=["MLFlow name:", "Comet.ml name:", "Prepared count:"],
267+
headers=["MLFlow name:", "Comet ML name:", "Prepared count:"],
210268
tablefmt="presto",
211269
)
212270
)
@@ -217,7 +275,7 @@ def prepare(self):
217275
# Upload or not?
218276
print("")
219277
if self.answer is None:
220-
upload = input("Upload prepared data to Comet.ml? [y/N] ") in ("Y", "y")
278+
upload = input("Upload prepared data to Comet ML? [y/N] ") in ("Y", "y")
221279
else:
222280
upload = self.answer
223281
print("")
@@ -232,8 +290,8 @@ def prepare(self):
232290

233291
LOGGER.info("")
234292
LOGGER.info(
235-
"""If you need support, you can contact us at http://chat.comet.ml/"""
236-
""" or https://comet.ml/docs/quick-start/#getting-support"""
293+
"""If you need support, you can contact us at http://chat.comet.com/"""
294+
""" or https://comet.com/docs/quick-start/#getting-support"""
237295
)
238296
LOGGER.info("")
239297

@@ -319,11 +377,11 @@ def prepare_single_mlflow_run(self, run, original_experiment_name):
319377
base_url = url_join(
320378
self.api_client.server_url, "/api/experiment/redirect"
321379
)
322-
tags["mlflow.parentRunUrl"] = format_url(
323-
base_url, experimentKey=tags["mlflow.parentRunId"]
380+
tags["mlflow.parentRunUrl"] = merge_url(
381+
base_url, {"experimentKey": tags["mlflow.parentRunId"]}
324382
)
325383

326-
# Save the original MLFlow experiment name too as Comet.ml project might
384+
# Save the original MLFlow experiment name too as Comet.com project might
327385
# get renamed
328386
tags["mlflow.experimentName"] = original_experiment_name
329387

@@ -449,7 +507,7 @@ def get_model_prefixes(self, artifact_list):
449507
return models
450508

451509
def upload(self, prepared_data):
452-
LOGGER.info("# Start uploading data to Comet.ml")
510+
LOGGER.info("# Start uploading data to Comet ML")
453511

454512
all_project_names = []
455513

@@ -494,7 +552,7 @@ def upload(self, prepared_data):
494552

495553
LOGGER.info("")
496554
LOGGER.info(
497-
"Explore your experiment data on Comet.ml with the following links:",
555+
"Explore your experiment data on Comet ML with the following links:",
498556
)
499557
if len(all_project_names) < 6:
500558
for project_name in all_project_names:
@@ -516,7 +574,7 @@ def upload(self, prepared_data):
516574

517575
LOGGER.info(
518576
"Get deeper instrumentation by adding Comet SDK to your project:"
519-
" https://comet.ml/docs/python-sdk/mlflow/"
577+
" https://comet.com/docs/python-sdk/mlflow/"
520578
)
521579
LOGGER.info("")
522580

@@ -593,7 +651,7 @@ def get_or_create_comet_project(self, exp):
593651

594652
def create_or_login(self):
595653
auth_api_client = get_comet_api_client(None)
596-
LOGGER.info("Please create a free Comet account with your email.")
654+
LOGGER.info("Please create a free Comet.com account with your email.")
597655
if self.email is None:
598656
email = input("Email: ")
599657
print("")
@@ -627,23 +685,23 @@ def create_or_login(self):
627685
Reporting.report("mlflow_new_user", api_key=new_account["apiKey"])
628686

629687
LOGGER.info(
630-
"A Comet.ml account has been created for you and an email was sent to"
688+
"A Comet.com account has been created for you and an email was sent to"
631689
" you to setup your password later."
632690
)
633691
save_api_key(new_account["apiKey"])
634692
LOGGER.info(
635-
"Your Comet API Key has been saved to ~/.comet.ini, it is also"
636-
" available on your Comet.ml dashboard."
693+
"Your Comet API Key has been saved to ~/.comet.config, it is also"
694+
" available on your Comet.com dashboard."
637695
)
638696
return (
639697
new_account["apiKey"],
640698
new_account["token"],
641699
)
642700
else:
643701
LOGGER.info(
644-
"An account already exists for this account, please input your API Key"
702+
"An account already exists for this email, please input your API Key"
645703
" below (you can find it in your Settings page,"
646-
" https://comet.ml/docs/quick-start/#getting-your-comet-api-key):"
704+
" https://comet.com/docs/quick-start/#getting-your-comet-api-key):"
647705
)
648706
api_key = input("API Key: ")
649707

@@ -663,3 +721,29 @@ def get_api_key_or_login(self, api_key):
663721
Reporting.report("mlflow_existing_user", api_key=api_key)
664722

665723
return (api_key, None)
724+
725+
def _log_authentication_error(self, mlflow_store_uri, context):
726+
"""Log helpful error message for MLflow authentication errors."""
727+
LOGGER.error("")
728+
LOGGER.error("MLflow authentication error detected when %s.", context)
729+
LOGGER.error("")
730+
if mlflow_store_uri and "databricks" in mlflow_store_uri.lower():
731+
LOGGER.error(
732+
"For Databricks MLflow stores, you need to set the DATABRICKS_TOKEN environment variable:"
733+
)
734+
LOGGER.error(
735+
" export DATABRICKS_TOKEN=your_databricks_personal_access_token"
736+
)
737+
LOGGER.error("")
738+
LOGGER.error("You can generate a token by:")
739+
LOGGER.error(" 1. Click on your user profile icon in the top-right corner")
740+
LOGGER.error(" 2. Select 'User Settings'")
741+
LOGGER.error(" 3. Go to the 'Access Tokens' tab")
742+
LOGGER.error(" 4. Click 'Generate New Token'")
743+
else:
744+
LOGGER.error("For authenticated MLflow stores, you may need to set:")
745+
LOGGER.error(" - DATABRICKS_TOKEN (for Databricks token-based auth)")
746+
LOGGER.error(
747+
" - MLFLOW_TRACKING_USERNAME and MLFLOW_TRACKING_PASSWORD (for basic auth)"
748+
)
749+
LOGGER.error("")

comet_for_mlflow/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def save_api_key(api_key):
109109
with open(config_path, "wt") as config_file:
110110
config_file.write("# Config file for Comet.ml\n")
111111
config_file.write(
112-
"# For help see https://www.comet.ml/docs/python-sdk/getting-started/\n"
112+
"# For help see https://www.comet.com/docs/python-sdk/getting-started/\n"
113113
)
114114
config_file.write("")
115115

examples/keras-example/run.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
from tensorflow.keras.models import Sequential
1212
from tensorflow.keras.preprocessing.text import Tokenizer
1313

14+
# When using when from databricks:
15+
mlflow.set_tracking_uri("databricks")
16+
mlflow.set_experiment("/Shared/keras_example")
17+
1418
mlflow.tensorflow.autolog()
1519

1620
max_words = 1000

tests/test_comet_for_mlflow.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@
88
from random import randint, random
99

1010
import responses
11-
from comet_ml.connection import url_join
11+
from comet_ml.utils import url_join
1212
from mlflow import active_run, end_run, log_artifacts, log_metric, log_param, tracking
1313

1414
from comet_for_mlflow import comet_for_mlflow
1515

1616
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
1717

18-
SERVER_ADDRESS = "https://www.comet.ml/clientlib/"
18+
SERVER_ADDRESS = "https://www.comet.com/clientlib/"
1919

2020

2121
def mlflow_example():

0 commit comments

Comments
 (0)