From 0c59811ee44c1dd34af423b08564fca42c601af1 Mon Sep 17 00:00:00 2001 From: aran-yogesh Date: Tue, 24 Feb 2026 16:41:33 -0800 Subject: [PATCH 1/2] feat: persist sandbox_id to thread metadata via client.threads.update --- apps/agent/agent/integrations/langsmith.py | 35 +++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/agent/agent/integrations/langsmith.py b/apps/agent/agent/integrations/langsmith.py index 2c7c63d7..f84c7768 100644 --- a/apps/agent/agent/integrations/langsmith.py +++ b/apps/agent/agent/integrations/langsmith.py @@ -63,11 +63,44 @@ def create_langsmith_sandbox( template_name, template_image = _get_sandbox_template_config() provider = LangSmithProvider(api_key=api_key) - return provider.get_or_create( + backend = provider.get_or_create( sandbox_id=sandbox_id, template=template_name, template_image=template_image, ) + _update_thread_sandbox_metadata(backend.id) + return backend + + +def _update_thread_sandbox_metadata(sandbox_id: str) -> None: + """Update thread metadata with sandbox_id.""" + try: + import asyncio + + from langgraph.config import get_config + from langgraph_sdk import get_client + + config = get_config() + thread_id = config.get("configurable", {}).get("thread_id") + if not thread_id: + return + client = get_client() + + async def _update() -> None: + await client.threads.update( + thread_id=thread_id, + metadata={"sandbox_id": sandbox_id}, + ) + + try: + loop = asyncio.get_running_loop() + except RuntimeError: + asyncio.run(_update()) + else: + loop.create_task(_update()) + except Exception: + # Best-effort: ignore failures (no config context, client unavailable, etc.) + pass class SandboxProvider(ABC): From 20f9b734c7db414baf43810ef2260d8cf9beaca4 Mon Sep 17 00:00:00 2001 From: aran-yogesh Date: Wed, 25 Feb 2026 09:48:16 -0800 Subject: [PATCH 2/2] refactor: remove duplicate sandbox_id metadata updates in server.py --- apps/agent/agent/server.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/apps/agent/agent/server.py b/apps/agent/agent/server.py index 337ce9f4..bbad1fae 100644 --- a/apps/agent/agent/server.py +++ b/apps/agent/agent/server.py @@ -255,13 +255,6 @@ async def get_agent(config: RunnableConfig) -> Pregel: # noqa: PLR0915 sandbox_backend = await asyncio.to_thread(create_langsmith_sandbox) logger.info("Sandbox created: %s", sandbox_backend.id) - # Update metadata immediately after sandbox creation so other callers - # can connect to the sandbox while we clone the repo - await client.threads.update( - thread_id=thread_id, - metadata={"sandbox_id": sandbox_backend.id}, - ) - repo_dir = None if repo_owner and repo_name: logger.info("Cloning repo %s/%s into sandbox", repo_owner, repo_name) @@ -299,11 +292,6 @@ async def get_agent(config: RunnableConfig) -> Pregel: # noqa: PLR0915 try: sandbox_backend = await asyncio.to_thread(create_langsmith_sandbox) logger.info("New sandbox created: %s", sandbox_backend.id) - - await client.threads.update( - thread_id=thread_id, - metadata={"sandbox_id": sandbox_backend.id}, - ) except Exception: logger.exception("Failed to create replacement sandbox") await client.threads.update(thread_id=thread_id, metadata={"sandbox_id": None})