From ac5c60fecc0095f60a1a72b5816543eae9a8391f Mon Sep 17 00:00:00 2001 From: Yiorgis Gozadinos Date: Wed, 18 Jun 2025 10:40:54 +0200 Subject: [PATCH] default sqlite when no path is given --- src/haiku/rag/client.py | 10 +++++++++- src/haiku/rag/config.py | 8 +++++++- src/haiku/rag/embeddings/__init__.py | 3 +-- src/haiku/rag/embeddings/voyageai.py | 23 +++++++++++++---------- src/haiku/rag/utils.py | 25 +++++++++++++++++++++++++ uv.lock | 6 +----- 6 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 src/haiku/rag/utils.py diff --git a/src/haiku/rag/client.py b/src/haiku/rag/client.py index 77693542..d25ddb2b 100644 --- a/src/haiku/rag/client.py +++ b/src/haiku/rag/client.py @@ -7,6 +7,7 @@ from urllib.parse import urlparse import httpx +from haiku.rag.config import Config from haiku.rag.reader import FileReader from haiku.rag.store.engine import Store from haiku.rag.store.models.chunk import Chunk @@ -18,8 +19,15 @@ from haiku.rag.store.repositories.document import DocumentRepository class HaikuRAG: """High-level haiku-rag client.""" - def __init__(self, db_path: Path | Literal[":memory:"]): + def __init__( + self, + db_path: Path | Literal[":memory:"] = Config.DEFAULT_DATA_DIR + / "haiku.rag.sqlite", + ): """Initialize the RAG client with a database path.""" + if isinstance(db_path, Path): + if not db_path.parent.exists(): + Path.mkdir(db_path.parent, parents=True) self.store = Store(db_path) self.document_repository = DocumentRepository(self.store) self.chunk_repository = ChunkRepository(self.store) diff --git a/src/haiku/rag/config.py b/src/haiku/rag/config.py index de22f209..ffbe3054 100644 --- a/src/haiku/rag/config.py +++ b/src/haiku/rag/config.py @@ -1,22 +1,28 @@ import os +from pathlib import Path from dotenv import load_dotenv from pydantic import BaseModel +from haiku.rag.utils import get_default_data_dir + load_dotenv() class AppConfig(BaseModel): ENV: str = "development" - OLLAMA_BASE_URL: str = "http://localhost:11434" + DEFAULT_DATA_DIR: Path = get_default_data_dir() EMBEDDING_PROVIDER: str = "ollama" EMBEDDING_MODEL: str = "mxbai-embed-large" EMBEDDING_VECTOR_DIM: int = 1024 + CHUNK_SIZE: int = 256 CHUNK_OVERLAP: int = 32 + OLLAMA_BASE_URL: str = "http://localhost:11434" + # Expose Config object for app to import Config = AppConfig.model_validate(os.environ) diff --git a/src/haiku/rag/embeddings/__init__.py b/src/haiku/rag/embeddings/__init__.py index f54a1978..a297bc79 100644 --- a/src/haiku/rag/embeddings/__init__.py +++ b/src/haiku/rag/embeddings/__init__.py @@ -1,7 +1,6 @@ from haiku.rag.config import Config from haiku.rag.embeddings.base import EmbedderBase from haiku.rag.embeddings.ollama import Embedder as OllamaEmbedder -from haiku.rag.embeddings.voyageai import Embedder as VoyageAIEmbedder def get_embedder() -> EmbedderBase: @@ -14,7 +13,7 @@ def get_embedder() -> EmbedderBase: if Config.EMBEDDING_PROVIDER == "voyageai": try: - import voyageai + from haiku.rag.embeddings.voyageai import Embedder as VoyageAIEmbedder except ImportError: raise ImportError( "VoyageAI embedder requires the 'voyageai' package. " diff --git a/src/haiku/rag/embeddings/voyageai.py b/src/haiku/rag/embeddings/voyageai.py index 188ec4a6..63b7da30 100644 --- a/src/haiku/rag/embeddings/voyageai.py +++ b/src/haiku/rag/embeddings/voyageai.py @@ -1,14 +1,17 @@ -from voyageai.client import Client +try: + from voyageai.client import Client # type: ignore -from haiku.rag.config import Config -from haiku.rag.embeddings.base import EmbedderBase + from haiku.rag.config import Config + from haiku.rag.embeddings.base import EmbedderBase + class Embedder(EmbedderBase): + _model: str = Config.EMBEDDING_MODEL + _vector_dim: int = 1024 -class Embedder(EmbedderBase): - _model: str = Config.EMBEDDING_MODEL - _vector_dim: int = 1024 + async def embed(self, text: str) -> list[float]: + client = Client() + res = client.embed([text], model=self._model, output_dtype="float") + return res.embeddings[0] # type: ignore[return-value] - async def embed(self, text: str) -> list[float]: - client = Client() - res = client.embed([text], model=self._model, output_dtype="float") - return res.embeddings[0] # type: ignore[return-value] +except ImportError: + pass diff --git a/src/haiku/rag/utils.py b/src/haiku/rag/utils.py new file mode 100644 index 00000000..03c160bf --- /dev/null +++ b/src/haiku/rag/utils.py @@ -0,0 +1,25 @@ +import sys +from pathlib import Path + + +def get_default_data_dir() -> Path: + """ + Get the user data directory for the current system platform. + + Linux: ~/.local/share/haiku.rag + macOS: ~/Library/Application Support/haiku.rag + Windows: C:/Users//AppData/Roaming/haiku.rag + + :return: User Data Path + :rtype: Path + """ + home = Path.home() + + system_paths = { + "win32": home / "AppData/Roaming/haiku.rag", + "linux": home / ".local/share/haiku.rag", + "darwin": home / "Library/Application Support/haiku.rag", + } + + data_path = system_paths[sys.platform] + return data_path diff --git a/uv.lock b/uv.lock index 8dcb2c20..d63b0215 100644 --- a/uv.lock +++ b/uv.lock @@ -480,9 +480,6 @@ dependencies = [ ] [package.optional-dependencies] -all = [ - { name = "voyageai" }, -] voyageai = [ { name = "voyageai" }, ] @@ -507,11 +504,10 @@ requires-dist = [ { name = "python-dotenv", specifier = ">=1.1.0" }, { name = "sqlite-vec", specifier = ">=0.1.6" }, { name = "tiktoken", specifier = ">=0.9.0" }, - { name = "voyageai", marker = "extra == 'all'", specifier = ">=0.3.2" }, { name = "voyageai", marker = "extra == 'voyageai'", specifier = ">=0.3.2" }, { name = "watchfiles", specifier = ">=1.1.0" }, ] -provides-extras = ["voyageai", "all"] +provides-extras = ["voyageai"] [package.metadata.requires-dev] dev = [