mirror of
https://github.com/volcengine/OpenViking.git
synced 2026-09-30 01:08:26 +08:00
* feat(ovpack): add v2 manifest and conflict policy Add a portable OVPack manifest for scalar metadata and make imports validate scope, derived files, and conflicts before writing. * fix(ovpack): remove import vectorize option Make OVPack imports always rebuild vectors in the target environment, keep legacy packages compatible, and reject unsupported manifest versions before writing. * fix(ovpack): remove force import alias Use on_conflict as the single OVPack import conflict policy and reject removed force inputs. * fix(ovpack): regenerate runtime vector metadata Keep type portable but stop exporting or applying created_at, updated_at, and active_count from OVPack manifests. * fix(ovpack): validate manifest contents * fix(ovpack): require manifests for imports * fix(ovpack): close manifest validation gaps * fix(ovpack): defer parent creation until validation passes * fix(ovpack): remove export size guard * fix(ovpack): support session and scope-root restores * docs(ovpack): document full backup migration * feat(ovpack): add backup restore workflow * fix(ovpack): validate import scope compatibility
207 lines
6.9 KiB
Python
207 lines
6.9 KiB
Python
# Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd.
|
|
# SPDX-License-Identifier: AGPL-3.0
|
|
"""
|
|
Unified exception classes for OpenViking.
|
|
|
|
Based on gRPC standard status codes for consistency across service boundaries.
|
|
"""
|
|
|
|
from typing import Optional
|
|
|
|
|
|
class OpenVikingError(Exception):
|
|
"""Base exception for all OpenViking errors."""
|
|
|
|
def __init__(self, message: str, code: str = "UNKNOWN", details: Optional[dict] = None):
|
|
super().__init__(message)
|
|
self.message = message
|
|
self.code = code
|
|
self.details = details or {}
|
|
|
|
|
|
# ============= Argument Errors =============
|
|
|
|
|
|
class InvalidArgumentError(OpenVikingError):
|
|
"""Invalid argument provided."""
|
|
|
|
def __init__(self, message: str, details: Optional[dict] = None):
|
|
super().__init__(message, code="INVALID_ARGUMENT", details=details)
|
|
|
|
|
|
class InvalidURIError(InvalidArgumentError):
|
|
"""Invalid Viking URI format."""
|
|
|
|
def __init__(self, uri: str, reason: str = ""):
|
|
message = f"Invalid URI: {uri}"
|
|
if reason:
|
|
message += f" ({reason})"
|
|
super().__init__(message, details={"uri": uri, "reason": reason})
|
|
self.code = "INVALID_URI"
|
|
|
|
|
|
class UnsupportedDirectoryFilesError(InvalidArgumentError):
|
|
"""Directory contains unsupported file types (used when strict=True)."""
|
|
|
|
def __init__(self, message: str, unsupported_files: list):
|
|
super().__init__(message, details={"unsupported_files": unsupported_files})
|
|
self.unsupported_files = unsupported_files
|
|
|
|
|
|
# ============= Resource Errors =============
|
|
|
|
|
|
class NotFoundError(OpenVikingError):
|
|
"""Resource not found."""
|
|
|
|
def __init__(self, resource: str, resource_type: str = "resource"):
|
|
details = {"type": resource_type}
|
|
if resource:
|
|
details["resource"] = resource
|
|
message = f"{resource_type.capitalize()} not found: {resource}"
|
|
else:
|
|
message = f"{resource_type.capitalize()} not found"
|
|
super().__init__(message, code="NOT_FOUND", details=details)
|
|
|
|
|
|
class AlreadyExistsError(OpenVikingError):
|
|
"""Resource already exists."""
|
|
|
|
def __init__(self, resource: str, resource_type: str = "resource"):
|
|
message = f"{resource_type.capitalize()} already exists: {resource}"
|
|
super().__init__(
|
|
message, code="ALREADY_EXISTS", details={"resource": resource, "type": resource_type}
|
|
)
|
|
|
|
|
|
class ConflictError(OpenVikingError):
|
|
"""Resource conflict (e.g., locked by another operation)."""
|
|
|
|
def __init__(self, message: str, resource: Optional[str] = None):
|
|
details = {"resource": resource} if resource else {}
|
|
super().__init__(message, code="CONFLICT", details=details)
|
|
|
|
|
|
class FailedPreconditionError(OpenVikingError):
|
|
"""Operation cannot proceed because a required precondition is unmet."""
|
|
|
|
def __init__(self, message: str, details: Optional[dict] = None):
|
|
super().__init__(message, code="FAILED_PRECONDITION", details=details)
|
|
|
|
|
|
class AbortedError(OpenVikingError):
|
|
"""Operation was aborted, typically due to a concurrency conflict."""
|
|
|
|
def __init__(self, message: str = "Operation aborted", details: Optional[dict] = None):
|
|
super().__init__(message, code="ABORTED", details=details)
|
|
|
|
|
|
# ============= Authentication Errors =============
|
|
|
|
|
|
class UnauthenticatedError(OpenVikingError):
|
|
"""Authentication required but not provided or invalid."""
|
|
|
|
def __init__(self, message: str = "Authentication required"):
|
|
super().__init__(message, code="UNAUTHENTICATED")
|
|
|
|
|
|
class PermissionDeniedError(OpenVikingError):
|
|
"""Permission denied for the requested operation."""
|
|
|
|
def __init__(self, message: str = "Permission denied", resource: Optional[str] = None):
|
|
details = {"resource": resource} if resource else {}
|
|
super().__init__(message, code="PERMISSION_DENIED", details=details)
|
|
|
|
|
|
# ============= Service Errors =============
|
|
|
|
|
|
class UnavailableError(OpenVikingError):
|
|
"""Service temporarily unavailable."""
|
|
|
|
def __init__(self, service: str = "service", reason: str = ""):
|
|
message = f"{service.capitalize()} unavailable"
|
|
if reason:
|
|
message += f": {reason}"
|
|
super().__init__(
|
|
message, code="UNAVAILABLE", details={"service": service, "reason": reason}
|
|
)
|
|
|
|
|
|
class ResourceExhaustedError(OpenVikingError):
|
|
"""Resource quota, rate limit, or billing capacity was exhausted."""
|
|
|
|
def __init__(self, message: str = "Resource exhausted", details: Optional[dict] = None):
|
|
super().__init__(message, code="RESOURCE_EXHAUSTED", details=details)
|
|
|
|
|
|
class InternalError(OpenVikingError):
|
|
"""Internal server error."""
|
|
|
|
def __init__(self, message: str = "Internal error", cause: Optional[Exception] = None):
|
|
details = {"cause": str(cause)} if cause else {}
|
|
super().__init__(message, code="INTERNAL", details=details)
|
|
|
|
|
|
class DeadlineExceededError(OpenVikingError):
|
|
"""Operation timed out."""
|
|
|
|
def __init__(self, operation: str = "operation", timeout: Optional[float] = None):
|
|
message = f"{operation.capitalize()} timed out"
|
|
if timeout:
|
|
message += f" after {timeout}s"
|
|
super().__init__(
|
|
message, code="DEADLINE_EXCEEDED", details={"operation": operation, "timeout": timeout}
|
|
)
|
|
|
|
|
|
class UnimplementedError(OpenVikingError):
|
|
"""Operation is not implemented."""
|
|
|
|
def __init__(self, message: str = "Operation not implemented", details: Optional[dict] = None):
|
|
super().__init__(message, code="UNIMPLEMENTED", details=details)
|
|
|
|
|
|
# ============= Business Errors =============
|
|
|
|
|
|
class ProcessingError(OpenVikingError):
|
|
"""Error during resource processing."""
|
|
|
|
def __init__(self, message: str, source: Optional[str] = None):
|
|
details = {"source": source} if source else {}
|
|
super().__init__(message, code="PROCESSING_ERROR", details=details)
|
|
|
|
|
|
class EmbeddingFailedError(ProcessingError):
|
|
"""Embedding generation failed."""
|
|
|
|
def __init__(self, message: str = "Embedding generation failed", source: Optional[str] = None):
|
|
super().__init__(message, source=source)
|
|
self.code = "EMBEDDING_FAILED"
|
|
|
|
|
|
class VLMFailedError(ProcessingError):
|
|
"""VLM processing failed."""
|
|
|
|
def __init__(self, message: str = "VLM processing failed", source: Optional[str] = None):
|
|
super().__init__(message, source=source)
|
|
self.code = "VLM_FAILED"
|
|
|
|
|
|
class SessionExpiredError(OpenVikingError):
|
|
"""Session has expired."""
|
|
|
|
def __init__(self, session_id: str):
|
|
message = f"Session expired: {session_id}"
|
|
super().__init__(message, code="SESSION_EXPIRED", details={"session_id": session_id})
|
|
|
|
|
|
class NotInitializedError(OpenVikingError):
|
|
"""Service or component not initialized."""
|
|
|
|
def __init__(self, component: str = "service"):
|
|
message = f"{component.capitalize()} not initialized. Call initialize() first."
|
|
super().__init__(message, code="NOT_INITIALIZED", details={"component": component})
|