Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 15 additions & 7 deletions langfuse/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,20 +342,28 @@ def _get_release_value(self, release: Optional[str] = None) -> Optional[str]:
else:
return get_common_release_envs()

def _get_project_id(self) -> Optional[str]:
"""Fetch and return the current project id. Persisted across requests. Returns None if no project id is found for api keys."""
if not self.project_id:
proj = self.client.projects.get()
if not proj.data or not proj.data[0].id:
return None

self.project_id = proj.data[0].id

return self.project_id

def get_trace_id(self) -> str:
"""Get the current trace id."""
return self.trace_id

def get_trace_url(self) -> str:
"""Get the URL of the current trace to view it in the Langfuse UI."""
if not self.project_id:
proj = self.client.projects.get()
if not proj.data or not proj.data[0].id:
return f"{self.base_url}/trace/{self.trace_id}"

self.project_id = proj.data[0].id
project_id = self._get_project_id()
if not project_id:
return f"{self.base_url}/trace/{self.trace_id}"

return f"{self.base_url}/project/{self.project_id}/traces/{self.trace_id}"
return f"{self.base_url}/project/{project_id}/traces/{self.trace_id}"

def get_dataset(
self, name: str, *, fetch_items_page_size: Optional[int] = 50
Expand Down
7 changes: 6 additions & 1 deletion langfuse/decorators/langfuse_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,12 @@ def get_current_trace_url(self) -> Optional[str]:
if not trace_id:
raise ValueError("No trace found in the current context")

return f"{self.client_instance.client._client_wrapper._base_url}/trace/{trace_id}"
project_id = self.client_instance._get_project_id()

if not project_id:
return f"{self.client_instance.client._client_wrapper._base_url}/trace/{trace_id}"

return f"{self.client_instance.client._client_wrapper._base_url}/project/{project_id}/traces/{trace_id}"

except Exception as e:
self._log.error(f"Failed to get current trace URL: {e}")
Expand Down
7 changes: 7 additions & 0 deletions tests/test_core_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,13 @@ def faulty_mask_func(data):
assert fetched_trace["output"] == "<fully masked due to failed mask function>"


def test_get_project_id():
langfuse = Langfuse(debug=False)
res = langfuse._get_project_id()
assert res is not None
assert res == "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a"


def test_generate_trace_id():
langfuse = Langfuse(debug=False)
trace_id = create_uuid()
Expand Down
24 changes: 24 additions & 0 deletions tests/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,30 @@ def level_1_function(*args, **kwargs):
)


def test_get_current_trace_url():
mock_trace_id = create_uuid()

@observe()
def level_3_function():
return langfuse_context.get_current_trace_url()

@observe()
def level_2_function():
return level_3_function()

@observe()
def level_1_function(*args, **kwargs):
return level_2_function()

result = level_1_function(
*mock_args, **mock_kwargs, langfuse_observation_id=mock_trace_id
)
langfuse_context.flush()

expected_url = f"http://localhost:3000/project/7a88fb47-b4e2-43b8-a06c-a5ce950dc53a/traces/{mock_trace_id}"
assert result == expected_url


def test_scoring_observations():
mock_name = "test_scoring_observations"
mock_trace_id = create_uuid()
Expand Down
Loading