From 93aee88aa4460676048e667e202c5d44cf706517 Mon Sep 17 00:00:00 2001 From: aran-yogesh Date: Thu, 12 Feb 2026 17:39:59 -0800 Subject: [PATCH] ix: resolve circular import by moving sandbox helpers to integrations --- apps/agent/agent/integrations/langsmith.py | 48 +++++++++++++++++ apps/agent/agent/server.py | 2 +- apps/agent/agent/tools/commit_and_open_pr.py | 2 +- apps/agent/agent/utils/sandbox.py | 55 -------------------- 4 files changed, 50 insertions(+), 57 deletions(-) delete mode 100644 apps/agent/agent/utils/sandbox.py diff --git a/apps/agent/agent/integrations/langsmith.py b/apps/agent/agent/integrations/langsmith.py index 7e90e11c..3bf23afd 100644 --- a/apps/agent/agent/integrations/langsmith.py +++ b/apps/agent/agent/integrations/langsmith.py @@ -23,6 +23,54 @@ from deepagents.backends.sandbox import BaseSandbox from langsmith.sandbox import Sandbox, SandboxClient, SandboxTemplate +def _get_langsmith_api_key() -> str | None: + """Get LangSmith API key from environment. + + Checks LANGSMITH_API_KEY first, then falls back to LANGSMITH_API_KEY_PROD + for LangGraph Cloud deployments where LANGSMITH_API_KEY is reserved. + """ + return os.environ.get("LANGSMITH_API_KEY") or os.environ.get("LANGSMITH_API_KEY_PROD") + + +def _get_sandbox_template_config() -> tuple[str | None, str | None]: + """Get sandbox template configuration from environment. + + Returns: + Tuple of (template_name, template_image) from environment variables. + Values are None if not set in environment. + """ + template_name = os.environ.get("DEFAULT_SANDBOX_TEMPLATE_NAME") + template_image = os.environ.get("DEFAULT_SANDBOX_TEMPLATE_IMAGE") + return template_name, template_image + + +def _create_langsmith_sandbox( + sandbox_id: str | None = None, +) -> SandboxBackendProtocol: + """Create or connect to a LangSmith sandbox without automatic cleanup. + + This function directly uses the LangSmithProvider to create/connect to sandboxes + without the context manager cleanup, allowing sandboxes to persist across + multiple agent invocations. + + Args: + sandbox_id: Optional existing sandbox ID to connect to. + If None, creates a new sandbox. + + Returns: + SandboxBackendProtocol instance + """ + api_key = _get_langsmith_api_key() + template_name, template_image = _get_sandbox_template_config() + + provider = LangSmithProvider(api_key=api_key) + return provider.get_or_create( + sandbox_id=sandbox_id, + template=template_name, + template_image=template_image, + ) + + class SandboxProvider(ABC): """Interface for creating and deleting sandbox backends.""" diff --git a/apps/agent/agent/server.py b/apps/agent/agent/server.py index a0dcd504..c44c76b4 100644 --- a/apps/agent/agent/server.py +++ b/apps/agent/agent/server.py @@ -36,7 +36,7 @@ from .middleware import ( ) from .prompt import construct_system_prompt from .tools import commit_and_open_pr, fetch_url, http_request -from .utils.sandbox import _create_langsmith_sandbox +from .integrations.langsmith import _create_langsmith_sandbox client = get_client() diff --git a/apps/agent/agent/tools/commit_and_open_pr.py b/apps/agent/agent/tools/commit_and_open_pr.py index 90b477cb..c6c9fe92 100644 --- a/apps/agent/agent/tools/commit_and_open_pr.py +++ b/apps/agent/agent/tools/commit_and_open_pr.py @@ -5,7 +5,7 @@ from typing import Any from langgraph.config import get_config from ..encryption import decrypt_token -from ..utils.sandbox import _create_langsmith_sandbox +from ..integrations.langsmith import _create_langsmith_sandbox from ..utils.github import ( create_github_pr, get_github_default_branch, diff --git a/apps/agent/agent/utils/sandbox.py b/apps/agent/agent/utils/sandbox.py deleted file mode 100644 index 371019b1..00000000 --- a/apps/agent/agent/utils/sandbox.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Sandbox creation and configuration utilities.""" - -import os - -from deepagents.backends.protocol import SandboxBackendProtocol - -from ..integrations.langsmith import LangSmithProvider - - -def _get_langsmith_api_key() -> str | None: - """Get LangSmith API key from environment. - - Checks LANGSMITH_API_KEY first, then falls back to LANGSMITH_API_KEY_PROD - for LangGraph Cloud deployments where LANGSMITH_API_KEY is reserved. - """ - return os.environ.get("LANGSMITH_API_KEY") or os.environ.get("LANGSMITH_API_KEY_PROD") - - -def _get_sandbox_template_config() -> tuple[str | None, str | None]: - """Get sandbox template configuration from environment. - - Returns: - Tuple of (template_name, template_image) from environment variables. - Values are None if not set in environment. - """ - template_name = os.environ.get("DEFAULT_SANDBOX_TEMPLATE_NAME") - template_image = os.environ.get("DEFAULT_SANDBOX_TEMPLATE_IMAGE") - return template_name, template_image - - -def _create_langsmith_sandbox( - sandbox_id: str | None = None, -) -> SandboxBackendProtocol: - """Create or connect to a LangSmith sandbox without automatic cleanup. - - This function directly uses the LangSmithProvider to create/connect to sandboxes - without the context manager cleanup, allowing sandboxes to persist across - multiple agent invocations. - - Args: - sandbox_id: Optional existing sandbox ID to connect to. - If None, creates a new sandbox. - - Returns: - SandboxBackendProtocol instance - """ - api_key = _get_langsmith_api_key() - template_name, template_image = _get_sandbox_template_config() - - provider = LangSmithProvider(api_key=api_key) - return provider.get_or_create( - sandbox_id=sandbox_id, - template=template_name, - template_image=template_image, - )