Skip to content

fix(sdk): use direct workspace-scoped URL for experiment links - #7587

Closed
zhoufengen wants to merge 2 commits into
comet-ml:mainfrom
zhoufengen:fix/experiment-url-direct-link
Closed

fix(sdk): use direct workspace-scoped URL for experiment links#7587
zhoufengen wants to merge 2 commits into
comet-ml:mainfrom
zhoufengen:fix/experiment-url-direct-link

Conversation

@zhoufengen

Copy link
Copy Markdown
Contributor

Summary

evaluate() and related entry points printed an opaque session-redirect URL (v1/session/redirect/experiments/?...) for experiment results. That URL is hard to copy into docs/notebooks/CI logs and breaks entirely in offline or air-gapped environments where the Opik server is unreachable.

This change mirrors the dataset URL fix (#7440): experiment links now point directly at the frontend compare page, matching the URL shown in the browser address bar.

Changes

  • url_helpers.get_experiment_url_by_id now builds the direct link opik/{workspace}/experiments/{dataset_id}/compare?experiments=[...] (JSON-array experiments query param, the format the compare page reads) instead of the legacy session-redirect path.
  • Updated its signature from (dataset_id, experiment_id, url_override) to (dataset_id, experiment_id, base_url, workspace).
  • Updated all 5 call sites in evaluator.py to pass base_url=client.config.url_override and workspace=client._dereferenced_workspace().

Testing

  • Added parametrized unit tests in tests/unit/test_url_helpers.py covering both base_url shapes (local and comet.com).
  • test_url_helpers.py passes: 15 passed (including the 2 new experiment-URL cases).
  • Verified with ruff check, ruff format --check, and mypy on the changed files.

Test suite passing on the fix branch:

tests pass

Related Issues

Closes #7462

evaluate() and related entry points logged an opaque session-redirect
URL (v1/session/redirect/experiments/...) for experiment results. This
breaks in offline/air-gapped environments and does not match the URL
shown in the browser address bar.

Mirror the dataset URL fix (comet-ml#7440): get_experiment_url_by_id now builds
the direct link opik/{workspace}/experiments/{dataset_id}/compare?experiments=[...]
and its 5 call sites pass base_url and workspace (via
client._dereferenced_workspace()).

Adds parametrized unit tests covering both base_url shapes.

Closes comet-ml#7462
Hosted via raw.githubusercontent.com so it renders inline in the PR body.
@zhoufengen
zhoufengen requested a review from a team as a code owner July 23, 2026 09:28
@github-actions github-actions Bot added python Pull requests that update Python code tests Including test files, or tests related like configuration. Python SDK 🟢 size/S labels Jul 23, 2026
Comment on lines 605 to 610
experiment_url = url_helpers.get_experiment_url_by_id(
experiment_id=experiment.id,
dataset_id=dataset.id,
url_override=client.config.url_override,
base_url=client.config.url_override,
workspace=client._dereferenced_workspace(),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link rendering aborts evaluation

_evaluate_task() resolves experiment_url through client._dereferenced_workspace() without guarding the workspace lookup, so a failed GET /v1/private/auth/workspace can raise after scoring succeeds and prevent client.flush() and experiment.log_experiment_scores(...) from running — should we catch that lookup or defer URL construction?

Severity

Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
sdks/python/src/opik/evaluation/evaluator.py around lines 605-610 inside
`_evaluate_task()`, `experiment_url = url_helpers.get_experiment_url_by_id(...)`
currently calls `workspace=client._dereferenced_workspace()` without protection, which
can trigger a real network GET for the workspace and raise after scoring has already
succeeded. Refactor this block to be best-effort: wrap the URL construction and
`report.display_experiment_link(...)` in a try/except that logs the failure and falls
back to a URL built without the workspace (or with a safe default), so the function
still reaches `client.flush()`,
`_try_notifying_about_experiment_completion(experiment)`, and
`experiment.log_experiment_scores(...)` and returns `evaluation_result_`. If possible,
ensure `client.flush()` and score logging occur in a finally/guaranteed path independent
of URL lookup errors.

Comment on lines 31 to +36
def get_experiment_url_by_id(
dataset_id: str, experiment_id: str, url_override: str
dataset_id: str, experiment_id: str, base_url: str, workspace: str
) -> str:
encoded_opik_url = base64.b64encode(url_override.encode("utf-8")).decode("utf-8")

project_path = urllib.parse.quote(
f"v1/session/redirect/experiments/?experiment_id={experiment_id}&dataset_id={dataset_id}&path={encoded_opik_url}",
safe=ALLOWED_URL_CHARACTERS,
)
return urllib.parse.urljoin(ensure_ending_slash(url_override), project_path)
domain_root = get_base_url(base_url)
experiments_param = urllib.parse.quote(json.dumps([experiment_id]))
return f"{domain_root}opik/{workspace}/experiments/{dataset_id}/compare?experiments={experiments_param}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Undocumented URL contract

get_experiment_url_by_id is still undocumented, so callers have to infer that base_url is normalized, workspace is embedded in the path, and experiment_id becomes ?experiments=[...], which makes the new URL contract easy to miss — should we add a short docstring here?

Severity

Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
sdks/python/src/opik/url_helpers.py around lines 31-36, update the
`get_experiment_url_by_id` function by adding a short docstring that documents the new
public contract. Explicitly state that `base_url` is normalized via `get_base_url`, that
`workspace` is embedded in the URL path, and that the `experiments` query parameter is a
JSON array containing only `experiment_id` (i.e., `experiments=["<id>"]`) and then
URL-encoded. Keep it brief but concrete so downstream callers know exactly what the
helper returns and what each argument affects.

@petrotiurin

Copy link
Copy Markdown
Contributor

Thanks so much for taking the time to put this together — really appreciate the effort! We ended up merging #7593 which addresses the same issue, so I'm going to close this one out to avoid duplicate work. Feel free to open a PR for anything else you spot — contributions like this are always welcome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Python SDK python Pull requests that update Python code 🟢 size/S tests Including test files, or tests related like configuration.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SDK] Fix experiment URL logged by evaluate() to use direct workspace-scoped URL

2 participants