fix: use thread-level LangSmith URLs instead of run-level URLs to fix broken trace links (#1217)

* fix: use thread-level LangSmith URLs instead of run-level URLs to fix broken trace links

* linting
This commit is contained in:
Aran Yogesh 2026-04-23 13:46:28 -07:00 • committed by GitHub
parent a3a40a1bec
commit fc3e492083
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 26 additions and 14 deletions

View file

@ -20,11 +20,13 @@ def _compose_langsmith_project_url() -> str:
return f"{host_url}/o/{tenant_id}/projects/p/{project_id}" return f"{host_url}/o/{tenant_id}/projects/p/{project_id}"
def get_langsmith_trace_url(run_id: str) -> str | None: def get_langsmith_trace_url(thread_id: str) -> str | None:
"""Build the LangSmith trace URL for a given run ID.""" """Build the LangSmith thread URL for a given thread ID."""
try: try:
base = _compose_langsmith_project_url() project_url = _compose_langsmith_project_url()
return f"{base}?peek={run_id}&peeked_trace={run_id}" return f"{project_url}/t/{thread_id}"
except Exception: # noqa: BLE001 except Exception: # noqa: BLE001
logger.warning("Failed to build LangSmith trace URL for run %s", run_id, exc_info=True) logger.warning(
"Failed to build LangSmith trace URL for thread %s", thread_id, exc_info=True
)
return None return None

View file

@ -63,15 +63,23 @@ async def comment_on_linear_issue(
return bool(result.get("commentCreate", {}).get("success")) return bool(result.get("commentCreate", {}).get("success"))
async def post_linear_trace_comment(issue_id: str, run_id: str, triggering_comment_id: str) -> None: async def post_linear_trace_comment(
issue_id: str, thread_id: str, triggering_comment_id: str
) -> None:
"""Post a trace URL comment on a Linear issue.""" """Post a trace URL comment on a Linear issue."""
trace_url = get_langsmith_trace_url(run_id) trace_url = get_langsmith_trace_url(thread_id)
if trace_url: if trace_url:
await comment_on_linear_issue( await comment_on_linear_issue(
issue_id, issue_id,
f"On it! [View trace]({trace_url})", f"On it! [View trace]({trace_url})",
parent_id=triggering_comment_id or None, parent_id=triggering_comment_id or None,
) )
else:
await comment_on_linear_issue(
issue_id,
"On it!",
parent_id=triggering_comment_id or None,
)
async def list_teams() -> dict[str, Any]: async def list_teams() -> dict[str, Any]:

View file

@ -365,10 +365,12 @@ async def fetch_slack_thread_messages(channel_id: str, thread_ts: str) -> list[d
return messages return messages
async def post_slack_trace_reply(channel_id: str, thread_ts: str, run_id: str) -> None: async def post_slack_trace_reply(channel_id: str, thread_ts: str, thread_id: str) -> None:
"""Post a trace URL reply in a Slack thread.""" """Post a trace URL reply in a Slack thread."""
trace_url = get_langsmith_trace_url(run_id) trace_url = get_langsmith_trace_url(thread_id)
if trace_url: if trace_url:
await post_slack_thread_reply( await post_slack_thread_reply(
channel_id, thread_ts, f"Working on it! <{trace_url}|View trace>" channel_id, thread_ts, f"Working on it! <{trace_url}|View trace>"
) )
else:
await post_slack_thread_reply(channel_id, thread_ts, "Working on it!")

View file

@ -675,13 +675,13 @@ async def process_linear_issue( # noqa: PLR0912, PLR0915
langgraph_client = get_client(url=LANGGRAPH_URL) langgraph_client = get_client(url=LANGGRAPH_URL)
runs = await langgraph_client.runs.list(thread_id, limit=1) runs = await langgraph_client.runs.list(thread_id, limit=1)
if runs: if runs:
await post_linear_trace_comment(issue_id, runs[0]["run_id"], triggering_comment_id) await post_linear_trace_comment(issue_id, thread_id, triggering_comment_id)
else: else:
logger.error("Failed to queue message for thread %s", thread_id) logger.error("Failed to queue message for thread %s", thread_id)
else: else:
logger.info("Creating LangGraph run for thread %s", thread_id) logger.info("Creating LangGraph run for thread %s", thread_id)
langgraph_client = get_client(url=LANGGRAPH_URL) langgraph_client = get_client(url=LANGGRAPH_URL)
run = await langgraph_client.runs.create( await langgraph_client.runs.create(
thread_id, thread_id,
"agent", "agent",
input={"messages": [{"role": "user", "content": content_blocks}]}, input={"messages": [{"role": "user", "content": content_blocks}]},
@ -689,7 +689,7 @@ async def process_linear_issue( # noqa: PLR0912, PLR0915
if_not_exists="create", if_not_exists="create",
) )
logger.info("LangGraph run created successfully for thread %s", thread_id) logger.info("LangGraph run created successfully for thread %s", thread_id)
await post_linear_trace_comment(issue_id, run["run_id"], triggering_comment_id) await post_linear_trace_comment(issue_id, thread_id, triggering_comment_id)
async def process_slack_mention(event_data: dict[str, Any], repo_config: dict[str, str]) -> None: async def process_slack_mention(event_data: dict[str, Any], repo_config: dict[str, str]) -> None:
@ -834,7 +834,7 @@ async def process_slack_mention(event_data: dict[str, Any], repo_config: dict[st
logger.error("Failed to queue Slack message for thread %s", thread_id) logger.error("Failed to queue Slack message for thread %s", thread_id)
return return
run = await langgraph_client.runs.create( await langgraph_client.runs.create(
thread_id, thread_id,
"agent", "agent",
input={"messages": [{"role": "user", "content": content_blocks}]}, input={"messages": [{"role": "user", "content": content_blocks}]},
@ -842,7 +842,7 @@ async def process_slack_mention(event_data: dict[str, Any], repo_config: dict[st
if_not_exists="create", if_not_exists="create",
multitask_strategy="interrupt", multitask_strategy="interrupt",
) )
await post_slack_trace_reply(channel_id, thread_ts, run["run_id"]) await post_slack_trace_reply(channel_id, thread_ts, thread_id)
def verify_linear_signature(body: bytes, signature: str, secret: str) -> bool: def verify_linear_signature(body: bytes, signature: str, secret: str) -> bool: