exceptions.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. from __future__ import annotations
  2. import httpx
  3. from typing import Literal
  4. class APIStatusError(Exception):
  5. """Raised when an API response has a status code of 4xx or 5xx."""
  6. response: httpx.Response
  7. status_code: int
  8. request_id: str | None
  9. def __init__(
  10. self, message: str, *, response: httpx.Response, body: object | None
  11. ) -> None:
  12. super().__init__(message)
  13. self.request = response.request
  14. self.body = body
  15. self.response = response
  16. self.status_code = response.status_code
  17. self.request_id = response.headers.get("x-request-id")
  18. class APIConnectionError(Exception):
  19. def __init__(
  20. self, *, message: str = "Connection error.", request: httpx.Request | None
  21. ) -> None:
  22. super().__init__(message)
  23. self.request = request
  24. class BadRequestError(APIStatusError):
  25. status_code: Literal[400] = 400 # pyright: ignore[reportIncompatibleVariableOverride]
  26. class AuthenticationError(APIStatusError):
  27. status_code: Literal[401] = 401 # pyright: ignore[reportIncompatibleVariableOverride]
  28. class PermissionDeniedError(APIStatusError):
  29. status_code: Literal[403] = 403 # pyright: ignore[reportIncompatibleVariableOverride]
  30. class NotFoundError(APIStatusError):
  31. status_code: Literal[404] = 404 # pyright: ignore[reportIncompatibleVariableOverride]
  32. class ConflictError(APIStatusError):
  33. status_code: Literal[409] = 409 # pyright: ignore[reportIncompatibleVariableOverride]
  34. class UnprocessableEntityError(APIStatusError):
  35. status_code: Literal[422] = 422 # pyright: ignore[reportIncompatibleVariableOverride]
  36. class RateLimitError(APIStatusError):
  37. status_code: Literal[429] = 429 # pyright: ignore[reportIncompatibleVariableOverride]
  38. class APITimeoutError(APIConnectionError):
  39. def __init__(self, request: httpx.Request | None) -> None:
  40. super().__init__(message="Request timed out.", request=request)
  41. class StorageNotInitializedError(RuntimeError):
  42. """Raised when storage operations are attempted before initialization."""
  43. def __init__(self, storage_type: str = "Storage"):
  44. super().__init__(
  45. f"{storage_type} not initialized. Please ensure proper initialization:\n"
  46. f"\n"
  47. f" rag = LightRAG(...)\n"
  48. f" await rag.initialize_storages() # Required - auto-initializes pipeline_status\n"
  49. f"\n"
  50. f"See: https://github.com/HKUDS/LightRAG#important-initialization-requirements"
  51. )
  52. class PipelineNotInitializedError(KeyError):
  53. """Raised when pipeline status is accessed before initialization."""
  54. def __init__(self, namespace: str = ""):
  55. msg = (
  56. f"Pipeline namespace '{namespace}' not found.\n"
  57. f"\n"
  58. f"Pipeline status should be auto-initialized by initialize_storages().\n"
  59. f"If you see this error, please ensure:\n"
  60. f"\n"
  61. f" 1. You called await rag.initialize_storages()\n"
  62. f" 2. For multi-workspace setups, each LightRAG instance was properly initialized\n"
  63. f"\n"
  64. f"Standard initialization:\n"
  65. f" rag = LightRAG(workspace='your_workspace')\n"
  66. f" await rag.initialize_storages() # Auto-initializes pipeline_status\n"
  67. f"\n"
  68. f"If you need manual control (advanced):\n"
  69. f" from lightrag.kg.shared_storage import initialize_pipeline_status\n"
  70. f" await initialize_pipeline_status(workspace='your_workspace')"
  71. )
  72. super().__init__(msg)
  73. class PipelineCancelledException(Exception):
  74. """Raised when pipeline processing is cancelled by user request."""
  75. def __init__(self, message: str = "User cancelled"):
  76. super().__init__(message)
  77. self.message = message
  78. class ChunkTokenLimitExceededError(ValueError):
  79. """Raised when a chunk exceeds the configured token limit."""
  80. def __init__(
  81. self,
  82. chunk_tokens: int,
  83. chunk_token_limit: int,
  84. chunk_preview: str | None = None,
  85. ) -> None:
  86. preview = chunk_preview.strip() if chunk_preview else None
  87. truncated_preview = preview[:80] if preview else None
  88. preview_note = f" Preview: '{truncated_preview}'" if truncated_preview else ""
  89. message = (
  90. f"Chunk token length {chunk_tokens} exceeds chunk_token_size {chunk_token_limit}."
  91. f"{preview_note}"
  92. )
  93. super().__init__(message)
  94. self.chunk_tokens = chunk_tokens
  95. self.chunk_token_limit = chunk_token_limit
  96. self.chunk_preview = truncated_preview
  97. class DataMigrationError(Exception):
  98. """Raised when data migration from legacy collection/table fails."""
  99. def __init__(self, message: str):
  100. super().__init__(message)
  101. self.message = message
  102. class MultimodalAnalysisError(RuntimeError):
  103. """Raised when multimodal analysis must fail the current document.
  104. Hard failures (missing required field, schema mismatch, model not
  105. available, sidecar already carries ``status="failure"``) bubble this
  106. exception so the pipeline marks the document failed instead of writing
  107. an unusable analyze result. Callers persist a ``status="failure"``
  108. sidecar entry alongside the raise so a re-run sees the failure.
  109. """