diff --git a/sdks/python/src/opik/api_objects/opik_client.py b/sdks/python/src/opik/api_objects/opik_client.py index cad296cb7..855ee755e 100644 --- a/sdks/python/src/opik/api_objects/opik_client.py +++ b/sdks/python/src/opik/api_objects/opik_client.py @@ -85,14 +85,16 @@ def _initialize_streamer( ) def _display_trace_url(self, workspace: str, project_name: str) -> None: - projects_url = url_helpers.get_projects_url(workspace=workspace) + project_url = url_helpers.get_project_url( + workspace=workspace, project_name=project_name + ) if ( self._project_name_most_recent_trace is None or self._project_name_most_recent_trace != project_name ): LOGGER.info( - f'Started logging traces to the "{project_name}" project at {projects_url}.' + f'Started logging traces to the "{project_name}" project at {project_url}.' ) self._project_name_most_recent_trace = project_name diff --git a/sdks/python/src/opik/url_helpers.py b/sdks/python/src/opik/url_helpers.py index b317eb3cd..78fb76b26 100644 --- a/sdks/python/src/opik/url_helpers.py +++ b/sdks/python/src/opik/url_helpers.py @@ -6,13 +6,14 @@ URL_ACCOUNT_DETAILS_POSTFIX: Final[str] = "api/rest/v2/account-details" URL_WORKSPACE_GET_LIST_POSTFIX: Final[str] = "api/rest/v2/workspaces" HEALTH_CHECK_URL_POSTFIX: Final[str] = "/is-alive/ping" +ALLOWED_URL_CHARACTERS: Final[str] = ":/&?=" def get_ui_url() -> str: config = opik.config.OpikConfig() opik_url_override = config.url_override - return opik_url_override.rstrip("/api") + return opik_url_override.rstrip("/api") + "/" def get_experiment_url(dataset_name: str, experiment_id: str) -> str: @@ -27,12 +28,30 @@ def get_experiment_url(dataset_name: str, experiment_id: str) -> str: config = opik.config.OpikConfig() ui_url = get_ui_url() - return f"{ui_url}/{config.workspace}/experiments/{dataset_id}/compare?experiments=%5B%22{experiment_id}%22%5D" + experiment_path = urllib.parse.quote( + f'{config.workspace}/experiments/{dataset_id}/compare?experiments=["{experiment_id}"]', + safe=ALLOWED_URL_CHARACTERS, + ) + + return urllib.parse.urljoin(ui_url, experiment_path) def get_projects_url(workspace: str) -> str: ui_url = get_ui_url() - return f"{ui_url}/{workspace}/projects/" + + projects_path = "{workspace}/projects" + + return urllib.parse.urljoin(ui_url, projects_path) + + +def get_project_url(workspace: str, project_name: str) -> str: + ui_url = get_ui_url() + + project_path = urllib.parse.quote( + f"{workspace}/redirect/projects?name={project_name}", + safe=ALLOWED_URL_CHARACTERS, + ) + return urllib.parse.urljoin(ui_url, project_path) def get_base_url(url: str) -> str: