diff --git a/src/haiku/rag/app.py b/src/haiku/rag/app.py index b8e3c6d0..371b571f 100644 --- a/src/haiku/rag/app.py +++ b/src/haiku/rag/app.py @@ -5,6 +5,7 @@ from rich.console import Console from rich.markdown import Markdown from haiku.rag.client import HaikuRAG +from haiku.rag.config import Config from haiku.rag.mcp import create_mcp_server from haiku.rag.monitor import FileWatcher from haiku.rag.store.models.chunk import Chunk @@ -93,7 +94,7 @@ class HaikuRAGApp: async def serve(self, transport: str | None = None): """Start the MCP server.""" - monitor = FileWatcher(paths=[]) + monitor = FileWatcher(paths=Config.MONITOR_DIRECTORIES) asyncio.create_task(monitor.observe()) server = create_mcp_server(self.db_path) diff --git a/src/haiku/rag/config.py b/src/haiku/rag/config.py index ffbe3054..6483a092 100644 --- a/src/haiku/rag/config.py +++ b/src/haiku/rag/config.py @@ -2,7 +2,7 @@ import os from pathlib import Path from dotenv import load_dotenv -from pydantic import BaseModel +from pydantic import BaseModel, field_validator from haiku.rag.utils import get_default_data_dir @@ -13,6 +13,7 @@ class AppConfig(BaseModel): ENV: str = "development" DEFAULT_DATA_DIR: Path = get_default_data_dir() + MONITOR_DIRECTORIES: list[Path] = [] EMBEDDING_PROVIDER: str = "ollama" EMBEDDING_MODEL: str = "mxbai-embed-large" @@ -23,6 +24,17 @@ class AppConfig(BaseModel): OLLAMA_BASE_URL: str = "http://localhost:11434" + @field_validator("MONITOR_DIRECTORIES", mode="before") + @classmethod + def parse_monitor_directories(cls, v): + if isinstance(v, str): + if not v.strip(): + return [] + return [ + Path(path.strip()).absolute() for path in v.split(",") if path.strip() + ] + return v + # Expose Config object for app to import Config = AppConfig.model_validate(os.environ) diff --git a/src/haiku/rag/monitor.py b/src/haiku/rag/monitor.py index dccdb764..be12f9d9 100644 --- a/src/haiku/rag/monitor.py +++ b/src/haiku/rag/monitor.py @@ -10,7 +10,7 @@ logger = get_logger() class FileFilter(DefaultFilter): - def __init__(self, *, ignore_paths: list[str | Path] | None = None) -> None: + def __init__(self, *, ignore_paths: list[Path] | None = None) -> None: self.extensions = tuple(FileReader.extensions) super().__init__(ignore_paths=ignore_paths) @@ -19,7 +19,7 @@ class FileFilter(DefaultFilter): class FileWatcher: - def __init__(self, paths: list[str | Path]): + def __init__(self, paths: list[Path]): self.paths = paths async def observe(self): @@ -37,10 +37,8 @@ class FileWatcher: elif change == Change.deleted: await self._delete_document(Path(path)) - async def refresh(self, paths: list[str | Path] | None = None): - if paths is None: - paths = self.paths - for path in paths: + async def refresh(self): + for path in self.paths: for f in Path(path).rglob("**/*"): if f.is_file() and f.suffix in FileReader.extensions: await self._upsert_document(f)