initial commit
This commit is contained in:
286
venv/Lib/site-packages/langsmith/sandbox/_exceptions.py
Normal file
286
venv/Lib/site-packages/langsmith/sandbox/_exceptions.py
Normal file
@@ -0,0 +1,286 @@
|
||||
"""Custom exceptions for the sandbox client.
|
||||
|
||||
All sandbox exceptions extend LangSmithError for unified error handling.
|
||||
The exceptions are organized by error type rather than resource type,
|
||||
with a resource_type attribute for specific handling when needed.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from langsmith.utils import LangSmithError
|
||||
|
||||
|
||||
class SandboxClientError(LangSmithError):
|
||||
"""Base exception for sandbox client errors."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Connection and Authentication Errors
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class SandboxAPIError(SandboxClientError):
|
||||
"""Raised when the API endpoint returns an unexpected error.
|
||||
|
||||
For example, this is raised for wrong URL or path.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SandboxAuthenticationError(SandboxClientError):
|
||||
"""Raised when authentication fails (invalid or missing API key)."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SandboxConnectionError(SandboxClientError):
|
||||
"""Raised when connection to the sandbox server fails."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SandboxServerReloadError(SandboxConnectionError):
|
||||
"""Raised when the server sends a 1001 Going Away close frame.
|
||||
|
||||
This indicates a server hot-reload, not a true connection failure.
|
||||
The command is still running on the server.
|
||||
|
||||
This is a subclass of SandboxConnectionError, so the auto-reconnect
|
||||
logic in CommandHandle catches it along with all other
|
||||
connection errors. The distinction matters for retry strategy:
|
||||
SandboxServerReloadError triggers immediate reconnect (no backoff),
|
||||
while other SandboxConnectionError triggers exponential backoff.
|
||||
|
||||
Users typically never see this exception — it's handled internally.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Resource Errors (type-based, with resource_type attribute)
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class ResourceNotFoundError(SandboxClientError):
|
||||
"""Raised when a resource is not found.
|
||||
|
||||
Attributes:
|
||||
resource_type: Type of resource (sandbox, template, volume, pool, file).
|
||||
"""
|
||||
|
||||
def __init__(self, message: str, resource_type: Optional[str] = None):
|
||||
"""Initialize the error."""
|
||||
super().__init__(message)
|
||||
self.resource_type = resource_type
|
||||
|
||||
|
||||
class ResourceTimeoutError(SandboxClientError):
|
||||
"""Raised when an operation times out.
|
||||
|
||||
Attributes:
|
||||
resource_type: Type of resource (sandbox, volume, pool).
|
||||
last_status: The last known status before timeout (for sandboxes).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
resource_type: Optional[str] = None,
|
||||
last_status: Optional[str] = None,
|
||||
):
|
||||
"""Initialize the error."""
|
||||
super().__init__(message)
|
||||
self.resource_type = resource_type
|
||||
self.last_status = last_status
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Return string representation."""
|
||||
base = super().__str__()
|
||||
if self.last_status:
|
||||
return f"{base} (last_status: {self.last_status})"
|
||||
return base
|
||||
|
||||
|
||||
class ResourceInUseError(SandboxClientError):
|
||||
"""Raised when deleting a resource that is still in use.
|
||||
|
||||
Attributes:
|
||||
resource_type: Type of resource (template, volume).
|
||||
"""
|
||||
|
||||
def __init__(self, message: str, resource_type: Optional[str] = None):
|
||||
"""Initialize the error."""
|
||||
super().__init__(message)
|
||||
self.resource_type = resource_type
|
||||
|
||||
|
||||
class ResourceAlreadyExistsError(SandboxClientError):
|
||||
"""Raised when creating a resource that already exists.
|
||||
|
||||
Attributes:
|
||||
resource_type: Type of resource (e.g., pool).
|
||||
"""
|
||||
|
||||
def __init__(self, message: str, resource_type: Optional[str] = None):
|
||||
"""Initialize the error."""
|
||||
super().__init__(message)
|
||||
self.resource_type = resource_type
|
||||
|
||||
|
||||
class ResourceNameConflictError(SandboxClientError):
|
||||
"""Raised when updating a resource name to one that already exists.
|
||||
|
||||
Attributes:
|
||||
resource_type: Type of resource (volume, template, pool, sandbox).
|
||||
"""
|
||||
|
||||
def __init__(self, message: str, resource_type: Optional[str] = None):
|
||||
"""Initialize the error."""
|
||||
super().__init__(message)
|
||||
self.resource_type = resource_type
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Validation and Quota Errors
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class ValidationError(SandboxClientError):
|
||||
"""Raised when request validation fails.
|
||||
|
||||
This includes:
|
||||
- Resource values exceeding server-defined limits (CPU, memory, storage)
|
||||
- Invalid resource units
|
||||
- Invalid name formats
|
||||
- Pool validation failures (e.g., template has volumes)
|
||||
|
||||
Attributes:
|
||||
field: The field that failed validation (e.g., "cpu", "memory").
|
||||
details: List of validation error details from the API.
|
||||
error_type: Machine-readable error type from the API.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
field: Optional[str] = None,
|
||||
details: Optional[list[dict]] = None,
|
||||
error_type: Optional[str] = None,
|
||||
):
|
||||
"""Initialize the error."""
|
||||
super().__init__(message)
|
||||
self.field = field
|
||||
self.details = details or []
|
||||
self.error_type = error_type
|
||||
|
||||
|
||||
class QuotaExceededError(SandboxClientError):
|
||||
"""Raised when organization quota limits are exceeded.
|
||||
|
||||
Users should contact support@langchain.dev to increase quotas.
|
||||
|
||||
Attributes:
|
||||
quota_type: Type of quota exceeded (e.g., "sandbox_count", "cpu").
|
||||
"""
|
||||
|
||||
def __init__(self, message: str, quota_type: Optional[str] = None):
|
||||
"""Initialize the error."""
|
||||
super().__init__(message)
|
||||
self.quota_type = quota_type
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Resource Creation Errors
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class ResourceCreationError(SandboxClientError):
|
||||
"""Raised when resource provisioning fails.
|
||||
|
||||
Attributes:
|
||||
resource_type: Type of resource (sandbox, volume, pool).
|
||||
error_type: Machine-readable error type (ImagePull, CrashLoop,
|
||||
SandboxConfig, Unschedulable, VolumeProvisioning).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
resource_type: Optional[str] = None,
|
||||
error_type: Optional[str] = None,
|
||||
):
|
||||
"""Initialize the error."""
|
||||
super().__init__(message)
|
||||
self.resource_type = resource_type
|
||||
self.error_type = error_type
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Return string representation."""
|
||||
if self.error_type:
|
||||
return f"{super().__str__()} [{self.error_type}]"
|
||||
return super().__str__()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Sandbox Operation Errors (runtime errors during sandbox interaction)
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class DataplaneNotConfiguredError(SandboxClientError):
|
||||
"""Raised when dataplane_url is not available for the sandbox.
|
||||
|
||||
This occurs when the sandbox-router URL is not configured for the cluster.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SandboxNotReadyError(SandboxClientError):
|
||||
"""Raised when attempting to interact with a sandbox that is not ready."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SandboxOperationError(SandboxClientError):
|
||||
"""Raised when a sandbox operation fails (run, read, write).
|
||||
|
||||
Attributes:
|
||||
operation: The operation that failed (command, read, write).
|
||||
error_type: Machine-readable error type from the API.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
operation: Optional[str] = None,
|
||||
error_type: Optional[str] = None,
|
||||
):
|
||||
"""Initialize the error."""
|
||||
super().__init__(message)
|
||||
self.operation = operation
|
||||
self.error_type = error_type
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Return string representation."""
|
||||
if self.error_type:
|
||||
return f"{super().__str__()} [{self.error_type}]"
|
||||
return super().__str__()
|
||||
|
||||
|
||||
class CommandTimeoutError(SandboxOperationError):
|
||||
"""Raised when a command exceeds its timeout.
|
||||
|
||||
Attributes:
|
||||
timeout: The timeout value in seconds that was exceeded.
|
||||
"""
|
||||
|
||||
def __init__(self, message: str, timeout: Optional[int] = None):
|
||||
"""Initialize the error."""
|
||||
super().__init__(message, operation="command", error_type="CommandTimeout")
|
||||
self.timeout = timeout
|
||||
Reference in New Issue
Block a user