- LightRAG helps developers with both building and optimizing Retriever-Agent-Generator (RAG) pipelines.
+ LightRAG helps developers with both building and optimizing Retriever-Agent-Generator pipelines.
It is light, modular, and robust, with a 100% readable codebase.
diff --git a/lightrag/.gitignore b/lightrag/.gitignore
new file mode 100644
index 00000000..799780fd
--- /dev/null
+++ b/lightrag/.gitignore
@@ -0,0 +1 @@
+test*.py
diff --git a/lightrag/lightrag/components/model_client/anthropic_client.py b/lightrag/lightrag/components/model_client/anthropic_client.py
index c53049ef..869d6a2b 100644
--- a/lightrag/lightrag/components/model_client/anthropic_client.py
+++ b/lightrag/lightrag/components/model_client/anthropic_client.py
@@ -34,6 +34,9 @@ def get_first_message_content(completion: Message) -> str:
return completion.content[0].text
+__all__ = ["AnthropicAPIClient", "get_first_message_content"]
+
+
# NOTE: using customize parser might make the new_component more complex when we have to handle a callable
class AnthropicAPIClient(ModelClient):
__doc__ = r"""A component wrapper for the Anthropic API client.
diff --git a/lightrag/lightrag/components/model_client/cohere_client.py b/lightrag/lightrag/components/model_client/cohere_client.py
index 9d6dc00d..93132234 100644
--- a/lightrag/lightrag/components/model_client/cohere_client.py
+++ b/lightrag/lightrag/components/model_client/cohere_client.py
@@ -126,7 +126,8 @@ async def acall(
):
if self.async_client is None:
self.init_async_client()
- assert "model" in api_kwargs, "model must be specified"
+ if "model" not in api_kwargs:
+ raise ValueError("model must be specified")
if model_type == ModelType.RERANKER:
response = await self.async_client.rerank(**api_kwargs)
top_k_scores = [result.relevance_score for result in response.results]
diff --git a/lightrag/lightrag/components/model_client/openai_client.py b/lightrag/lightrag/components/model_client/openai_client.py
index b38addda..d374d123 100644
--- a/lightrag/lightrag/components/model_client/openai_client.py
+++ b/lightrag/lightrag/components/model_client/openai_client.py
@@ -118,7 +118,6 @@ def parse_chat_completion(self, completion: Completion) -> Any:
"""Parse the completion to a str."""
log.debug(f"completion: {completion}")
return self.chat_completion_parser(completion)
- # return completion.choices[0].message.content
def parse_embedding_response(
self, response: CreateEmbeddingResponse
diff --git a/lightrag/lightrag/components/model_client/utils.py b/lightrag/lightrag/components/model_client/utils.py
index a7bec9aa..2b81d96e 100644
--- a/lightrag/lightrag/components/model_client/utils.py
+++ b/lightrag/lightrag/components/model_client/utils.py
@@ -1,5 +1,4 @@
"Helpers for model client for integrating models and parsing the output."
-
from lightrag.core.types import EmbedderOutput, Embedding, Usage
diff --git a/lightrag/lightrag/core/__init__.py b/lightrag/lightrag/core/__init__.py
index aa246d6b..da3351f0 100644
--- a/lightrag/lightrag/core/__init__.py
+++ b/lightrag/lightrag/core/__init__.py
@@ -15,7 +15,7 @@
from .types import (
ModelType,
- ModelClientType,
+ # ModelClientType,
get_model_args,
Embedding,
Usage,
@@ -65,7 +65,7 @@
"GeneratorOutput",
"GeneratorOutputType",
"ModelType",
- "ModelClientType",
+ # "ModelClientType",
"get_model_args",
"Embedding",
"Usage",
diff --git a/lightrag/lightrag/database/sqlalchemy/model.py b/lightrag/lightrag/database/sqlalchemy/model.py
index 2b56f47e..7ba1b728 100644
--- a/lightrag/lightrag/database/sqlalchemy/model.py
+++ b/lightrag/lightrag/database/sqlalchemy/model.py
@@ -30,7 +30,14 @@
from typing import Optional, Dict, List
from datetime import datetime
import logging
+from lightrag.utils.lazy_import import safe_import, OptionalPackages
+sqlalchemy = safe_import(
+ OptionalPackages.SQLALCHEMY.value[0], OptionalPackages.SQLALCHEMY.value[1]
+)
+pgvector = safe_import(
+ OptionalPackages.PGVECTOR.value[0], OptionalPackages.PGVECTOR.value[1]
+)
from sqlalchemy import (
Column,
Integer,
@@ -48,11 +55,8 @@
from pgvector.sqlalchemy import Vector
-from lightrag.utils.lazy_import import safe_import, OptionalPackages
from lightrag.database.sqlalchemy.base import Base
-safe_import(OptionalPackages.SQLALCHEMY.value[0], OptionalPackages.SQLALCHEMY.value[1])
-safe_import(OptionalPackages.PGVECTOR.value[0], OptionalPackages.PGVECTOR.value[1])
log = logging.getLogger(__name__)
diff --git a/lightrag/lightrag/utils/lazy_import.py b/lightrag/lightrag/utils/lazy_import.py
index 18741b68..db0d1293 100644
--- a/lightrag/lightrag/utils/lazy_import.py
+++ b/lightrag/lightrag/utils/lazy_import.py
@@ -2,6 +2,7 @@
import importlib
import logging
+from types import ModuleType
from enum import Enum
@@ -65,7 +66,16 @@ class LazyImport:
optional_package (OptionalPackages): The optional package to import, it helps define the package name and error message.
"""
- def __init__(self, import_path: str, optional_package: OptionalPackages):
+ def __init__(
+ self, import_path: str, optional_package: OptionalPackages, *args, **kwargs
+ ):
+ if args or kwargs:
+ raise TypeError(
+ "LazyImport does not support subclassing or additional arguments. "
+ "Import the class directly from its specific module instead. For example, "
+ "from lightrag.components.model_client.cohere_client import CohereAPIClient"
+ "instead of using the lazy import with: from lightrag.components.model_client import CohereAPIClient"
+ )
self.import_path = import_path
self.optional_package = optional_package
self.module = None
@@ -100,10 +110,34 @@ def __call__(self, *args, **kwargs):
return self.class_(*args, **kwargs)
-def safe_import(module_name, install_message):
+def safe_import(module_name: str, install_message: str) -> ModuleType:
"""Safely import a module and raise an ImportError with the install message if the module is not found.
Mainly used internally to import optional packages only when needed.
+
+ Example:
+
+ 1. Tests
+
+ .. code-block:: python
+
+ try:
+ numpy = safe_import("numpy", "Please install numpy with: pip install numpy")
+ print(numpy.__version__)
+ except ImportError as e:
+ print(e)
+
+ When numpy is not installed, it will raise an ImportError with the install message.
+ When numpy is installed, it will print the numpy version.
+
+ 2. Use it to delay the import of optional packages in the library.
+
+ .. code-block:: python
+
+ from lightrag.utils.lazy_import import safe_import, OptionalPackages
+
+ numpy = safe_import(OptionalPackages.NUMPY.value[0], OptionalPackages.NUMPY.value[1])
+
"""
try:
return importlib.import_module(module_name)
diff --git a/lightrag/lightrag/utils/setup_env.py b/lightrag/lightrag/utils/setup_env.py
index a201d070..d2250e26 100644
--- a/lightrag/lightrag/utils/setup_env.py
+++ b/lightrag/lightrag/utils/setup_env.py
@@ -1,7 +1,18 @@
import dotenv
+import os
+import logging
+
+log = logging.getLogger(__name__)
def setup_env(dotenv_path: str = ".env"):
"""Load environment variables from .env file."""
- dotenv.load_dotenv(dotenv_path=dotenv_path)
+ if not os.path.exists(dotenv_path):
+ raise FileNotFoundError(f"File not found: {dotenv_path}")
+
+ try:
+ dotenv.load_dotenv(dotenv_path=dotenv_path, verbose=True, override=False)
+ except Exception as e:
+ log.error(f"Error loading .env file: {e}")
+ raise e
diff --git a/lightrag/poetry.lock b/lightrag/poetry.lock
index 1fb66df7..02d8f4b7 100644
--- a/lightrag/poetry.lock
+++ b/lightrag/poetry.lock
@@ -507,13 +507,13 @@ typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "t
[[package]]
name = "identify"
-version = "2.5.36"
+version = "2.6.0"
description = "File identification library for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"},
- {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"},
+ {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"},
+ {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"},
]
[package.extras]
@@ -966,13 +966,13 @@ files = [
[[package]]
name = "openai"
-version = "1.35.10"
+version = "1.35.12"
description = "The official Python library for the openai API"
optional = false
python-versions = ">=3.7.1"
files = [
- {file = "openai-1.35.10-py3-none-any.whl", hash = "sha256:962cb5c23224b5cbd16078308dabab97a08b0a5ad736a4fdb3dc2ffc44ac974f"},
- {file = "openai-1.35.10.tar.gz", hash = "sha256:85966949f4f960f3e4b239a659f9fd64d3a97ecc43c44dc0a044b5c7f11cccc6"},
+ {file = "openai-1.35.12-py3-none-any.whl", hash = "sha256:c3a8c9be524480ae32a3212b78d90786b112844eb8f96235ebb8bc5f35cb1e72"},
+ {file = "openai-1.35.12.tar.gz", hash = "sha256:2dd0f1d9ff34bf6bc89d15246245369d281b2ba01ae07eae5555a00da4b51e0b"},
]
[package.dependencies]
@@ -1614,17 +1614,20 @@ sqlcipher = ["sqlcipher3_binary"]
[[package]]
name = "sympy"
-version = "1.12.1"
+version = "1.13.0"
description = "Computer algebra system (CAS) in Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "sympy-1.12.1-py3-none-any.whl", hash = "sha256:9b2cbc7f1a640289430e13d2a56f02f867a1da0190f2f99d8968c2f74da0e515"},
- {file = "sympy-1.12.1.tar.gz", hash = "sha256:2877b03f998cd8c08f07cd0de5b767119cd3ef40d09f41c30d722f6686b0fb88"},
+ {file = "sympy-1.13.0-py3-none-any.whl", hash = "sha256:6b0b32a4673fb91bd3cac3b55406c8e01d53ae22780be467301cc452f6680c92"},
+ {file = "sympy-1.13.0.tar.gz", hash = "sha256:3b6af8f4d008b9a1a6a4268b335b984b23835f26d1d60b0526ebc71d48a25f57"},
]
[package.dependencies]
-mpmath = ">=1.1.0,<1.4.0"
+mpmath = ">=1.1.0,<1.4"
+
+[package.extras]
+dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"]
[[package]]
name = "tbb"
diff --git a/lightrag/pyproject.toml b/lightrag/pyproject.toml
index bc8c91d7..44d6a9de 100644
--- a/lightrag/pyproject.toml
+++ b/lightrag/pyproject.toml
@@ -2,7 +2,7 @@
name = "lightrag"
version = "0.0.0-alpha.16"
-description = "The 'PyTorch' library for LLM applications. RAG=Retriever-Agent-Generator."
+description = "The 'PyTorch' library for LLM applications."
authors = ["Li Yin