Skip to content

Commit affba09

Browse files
authored
[OPIK-5496] [SDK] configure(force=True) still prompts interactively for workspace (#6033)
1 parent 24aa777 commit affba09

3 files changed

Lines changed: 98 additions & 22 deletions

File tree

sdks/python/src/opik/configurator/configure.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def _configure_cloud(self) -> None:
9292

9393
# Update configuration if either API key or workspace has changed
9494
if update_config_with_api_key or update_config_with_workspace:
95-
self._update_config()
95+
self._update_config(save_to_file=True)
9696
else:
9797
self._update_config(save_to_file=False)
9898
_set_environment_variables_for_integrations(
@@ -152,7 +152,7 @@ def _configure_local(self) -> None:
152152

153153
if use_url:
154154
self.base_url = OPIK_BASE_URL_LOCAL
155-
self._update_config()
155+
self._update_config(save_to_file=True)
156156
self._log_project_configuration_message()
157157
return
158158

@@ -162,7 +162,7 @@ def _configure_local(self) -> None:
162162
"Opik URL is not specified - Please set your Opik instance URL using the environment variable OPIK_URL_OVERRIDE or provide it as an argument. For more details, refer to the documentation: https://www.comet.com/docs/opik/tracing/sdk_configuration."
163163
)
164164
self._ask_for_url()
165-
self._update_config()
165+
self._update_config(save_to_file=True)
166166
self._log_project_configuration_message()
167167

168168
def _set_api_key(self) -> bool:
@@ -186,6 +186,16 @@ def _set_api_key(self) -> bool:
186186
self._try_set_url_from_api_key()
187187
config_file_needs_updating = True if self.force else False
188188

189+
if (
190+
not config_file_needs_updating
191+
and self.current_config.api_key is not None
192+
):
193+
LOGGER.warning(
194+
"You already have an API key set in the configuration file. "
195+
"If you want to change it, please use the --force flag or force=True when calling the configure() method. "
196+
"Otherwise, the configuration file will not be updated but the session will use the new API key."
197+
)
198+
189199
elif self.force and self.api_key is None:
190200
self._ask_for_api_key()
191201
self._try_set_url_from_api_key()
@@ -294,7 +304,7 @@ def _set_workspace(self) -> bool:
294304
default_workspace = self._get_default_workspace()
295305
use_default_workspace = (
296306
True
297-
if self.automatic_approvals
307+
if self.automatic_approvals or self.force
298308
else ask_user_for_approval(
299309
f'Do you want to use "{default_workspace}" workspace? (Y/n)'
300310
)
@@ -379,7 +389,7 @@ def _ask_for_workspace(self) -> None:
379389
"User does not have access to the workspaces provided."
380390
)
381391

382-
def _update_config(self, save_to_file: bool = True) -> None:
392+
def _update_config(self, save_to_file: bool) -> None:
383393
"""
384394
Save changes to the config file and update the current session configuration.
385395

sdks/python/tests/library_integration/langchain/test_langgraph.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,12 @@ def respond_to_greeting(state: State) -> Dict[str, Any]:
8484
input=initial_state,
8585
output=result,
8686
tags=["tag1", "tag2"],
87-
metadata={
88-
"a": "b",
89-
"created_from": "langchain",
90-
},
87+
metadata=ANY_DICT.containing(
88+
{
89+
"a": "b",
90+
"created_from": "langchain",
91+
}
92+
),
9193
start_time=ANY_BUT_NONE,
9294
end_time=ANY_BUT_NONE,
9395
last_updated_at=ANY_BUT_NONE,
@@ -200,10 +202,12 @@ def invoke_graph_from_tracked_function(
200202
input=initial_state,
201203
output=result,
202204
tags=["tag1", "tag2"],
203-
metadata={
204-
"a": "b",
205-
"created_from": "langchain",
206-
},
205+
metadata=ANY_DICT.containing(
206+
{
207+
"a": "b",
208+
"created_from": "langchain",
209+
}
210+
),
207211
start_time=ANY_BUT_NONE,
208212
end_time=ANY_BUT_NONE,
209213
spans=[
@@ -782,7 +786,9 @@ def respond_to_greeting(state: State) -> Dict[str, Any]:
782786
"response": "Hello! How can I help you today?",
783787
},
784788
tags=["tag1", "tag2"],
785-
metadata={"a": "b", "created_from": "langchain"},
789+
metadata=ANY_DICT.containing(
790+
{"a": "b", "created_from": "langchain"}
791+
),
786792
type="general",
787793
end_time=ANY_BUT_NONE,
788794
project_name="langgraph-integration-test--distributed-headers",
@@ -933,7 +939,9 @@ def respond_to_greeting(state: State) -> Dict[str, Any]:
933939
"response": "Hello! How can I help you today?",
934940
},
935941
tags=["tag1", "tag2"],
936-
metadata={"a": "b", "created_from": "langchain"},
942+
metadata=ANY_DICT.containing(
943+
{"a": "b", "created_from": "langchain"}
944+
),
937945
type="general",
938946
end_time=ANY_BUT_NONE,
939947
project_name="Default Project",
@@ -1072,7 +1080,7 @@ def respond_to_greeting(state: State) -> Dict[str, Any]:
10721080
"response": "Hello! How can I help you today?",
10731081
},
10741082
tags=["tag1", "tag2"],
1075-
metadata={"a": "b", "created_from": "langchain"},
1083+
metadata=ANY_DICT.containing({"a": "b", "created_from": "langchain"}),
10761084
type="general",
10771085
end_time=ANY_BUT_NONE,
10781086
project_name="Default Project",

sdks/python/tests/unit/configurator/test_configure.py

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def test_update_config_success(self, mock_update_session_config, mock_opik_confi
147147
url = "http://example.com"
148148
workspace = "workspace1"
149149

150-
OpikConfigurator(api_key, workspace, url)._update_config()
150+
OpikConfigurator(api_key, workspace, url)._update_config(save_to_file=True)
151151

152152
# Ensure config object is created and saved
153153
mock_opik_config.assert_called_with(
@@ -179,7 +179,7 @@ def test_update_config_raises_exception(
179179
workspace = "workspace1"
180180

181181
with pytest.raises(ConfigurationError, match="Failed to update configuration."):
182-
OpikConfigurator(api_key, workspace, url)._update_config()
182+
OpikConfigurator(api_key, workspace, url)._update_config(save_to_file=True)
183183

184184
# Ensure save_to_file is not called due to the exception
185185
mock_update_session_config.assert_not_called()
@@ -201,7 +201,7 @@ def test_update_config_session_update_failure(
201201
workspace = "workspace1"
202202

203203
with pytest.raises(ConfigurationError, match="Failed to update configuration."):
204-
OpikConfigurator(api_key, workspace, url)._update_config()
204+
OpikConfigurator(api_key, workspace, url)._update_config(save_to_file=True)
205205

206206
# Ensure config object is created and saved
207207
mock_opik_config.assert_any_call(
@@ -567,6 +567,36 @@ def test_get_api_key_provided_key(self, mock_is_api_key_correct):
567567
assert configurator.api_key == "config_api_key"
568568
assert needs_update is True
569569

570+
@patch("opik.configurator.configure.LOGGER.warning")
571+
@patch("opik.configurator.configure.opik.config.OpikConfig")
572+
@patch(
573+
"opik.configurator.configure.opik_rest_helpers.is_api_key_correct",
574+
return_value=True,
575+
)
576+
def test_set_api_key__provided_api_key__another_key_already_set_in_config__not_forced__warning_is_shown(
577+
self, mock_is_api_key_correct, mock_opik_config, mock_logger_warning
578+
):
579+
"""
580+
Test that a warning is logged when an API key is provided, but one is already
581+
set in the configuration file and force=False.
582+
"""
583+
mock_config_instance = MagicMock()
584+
mock_config_instance.api_key = "existing_api_key"
585+
mock_opik_config.return_value = mock_config_instance
586+
587+
configurator = OpikConfigurator(
588+
api_key="new_api_key", url=OPIK_BASE_URL_LOCAL, force=False
589+
)
590+
needs_update = configurator._set_api_key()
591+
592+
mock_logger_warning.assert_called_once_with(
593+
"You already have an API key set in the configuration file. "
594+
"If you want to change it, please use the --force flag or force=True when calling the configure() method. "
595+
"Otherwise, the configuration file will not be updated but the session will use the new API key."
596+
)
597+
assert configurator.api_key == "new_api_key"
598+
assert needs_update is False
599+
570600
@patch(
571601
"opik.configurator.configure.opik_rest_helpers.is_api_key_correct",
572602
return_value=True,
@@ -785,6 +815,34 @@ def test_get_workspace_accept_default__automatic_approvals_enabled__no_approval_
785815
mock_get_default_workspace.assert_called_once_with()
786816
mock_ask_user_for_approval.assert_not_called()
787817

818+
@patch("opik.configurator.configure.opik.config.OpikConfig")
819+
@patch(
820+
"opik.configurator.configure.OpikConfigurator._get_default_workspace",
821+
return_value="default_workspace",
822+
)
823+
@patch("opik.configurator.configure.ask_user_for_approval", return_value=True)
824+
def test_get_workspace_accept_default__force_enabled__no_approval_questions_asked(
825+
self, mock_ask_user_for_approval, mock_get_default_workspace, mock_opik_config
826+
):
827+
"""
828+
Test that when force=True, the default workspace is used without asking for user approval.
829+
"""
830+
current_config = OpikConfig(workspace=OPIK_WORKSPACE_DEFAULT_NAME)
831+
mock_opik_config.return_value = current_config
832+
833+
configurator = OpikConfigurator(
834+
workspace=None,
835+
force=True,
836+
api_key="valid_api_key",
837+
automatic_approvals=False,
838+
)
839+
needs_update = configurator._set_workspace()
840+
841+
assert configurator.workspace == "default_workspace"
842+
assert needs_update is True
843+
mock_get_default_workspace.assert_called_once_with()
844+
mock_ask_user_for_approval.assert_not_called()
845+
788846
@patch("opik.configurator.configure.opik.config.OpikConfig")
789847
@patch(
790848
"opik.configurator.configure.OpikConfigurator._get_default_workspace",
@@ -1193,7 +1251,7 @@ def test_configure_local_uses_local_instance(
11931251
mock_ask_user_for_approval.assert_called_once_with(
11941252
f"Found local Opik instance on: {OPIK_BASE_URL_LOCAL}, do you want to use it? (Y/n)"
11951253
)
1196-
mock_update_config.assert_called_once_with()
1254+
mock_update_config.assert_called_once_with(save_to_file=True)
11971255

11981256
assert configurator.api_key is None
11991257
assert configurator.base_url == OPIK_BASE_URL_LOCAL
@@ -1220,7 +1278,7 @@ def test_configure_local_uses_local_instance__automatic_approvals_enabled__no_ap
12201278
configurator._configure_local()
12211279

12221280
mock_ask_user_for_approval.assert_not_called()
1223-
mock_update_config.assert_called_once_with()
1281+
mock_update_config.assert_called_once_with(save_to_file=True)
12241282

12251283
assert configurator.api_key is None
12261284
assert configurator.base_url == OPIK_BASE_URL_LOCAL
@@ -1271,7 +1329,7 @@ def test_configure_local_uses_local_instance__non_interactive__automatic_approva
12711329
configurator._configure_local()
12721330

12731331
mock_ask_user_for_approval.assert_not_called()
1274-
mock_update_config.assert_called_once_with()
1332+
mock_update_config.assert_called_once_with(save_to_file=True)
12751333

12761334
assert configurator.api_key is None
12771335
assert configurator.base_url == OPIK_BASE_URL_LOCAL

0 commit comments

Comments
 (0)