From 090be293d2cbbc0435dc6a8186301c85f9273015 Mon Sep 17 00:00:00 2001 From: Yiorgis Gozadinos Date: Fri, 19 Dec 2025 10:11:26 +0200 Subject: [PATCH] Clarify folder mounts in docker docs, check monitor paths exist in FileWatcher --- CHANGELOG.md | 9 ++++++++ docker/README.md | 26 +++++++++++++++++----- docs/installation.md | 7 ++++-- examples/docker/README.md | 25 ++++++++++++++++++++- haiku_rag_slim/haiku/rag/monitor.py | 12 ++++++++++ tests/test_monitor.py | 34 +++++++++++++++++++++++++++++ 6 files changed, 105 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db40a646..4650c7d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Changelog ## [Unreleased] +### Fixed + +- **File Monitor Path Validation**: Monitor now validates directories exist before watching ([#204](https://github.com/ggozad/haiku.rag/issues/204)) + - Provides clear error message pointing to `haiku.rag.yaml` configuration + - Prevents cryptic `FileNotFoundError: No path was found` from watchfiles +- **Docker Documentation**: Improved Docker setup instructions + - Added volume mount examples for config file and documents directory + - Clarified that `monitor.directories` must use container paths, not host paths + ## [0.21.0] - 2025-12-18 ### Added diff --git a/docker/README.md b/docker/README.md index 70917642..03053f38 100644 --- a/docker/README.md +++ b/docker/README.md @@ -44,19 +44,35 @@ Mount your config file and data directory: ```bash docker run -p 8001:8001 \ - -v $(pwd)/haiku.rag.yaml:/app/haiku.rag.yaml \ - -v $(pwd)/data:/data \ + -v /path/to/haiku.rag.yaml:/app/haiku.rag.yaml \ + -v /path/to/data:/data \ haiku-rag ``` -The container will automatically use the mounted `haiku.rag.yaml` configuration file. +To enable file monitoring, also mount a documents directory: + +```bash +docker run -p 8001:8001 \ + -v /path/to/haiku.rag.yaml:/app/haiku.rag.yaml \ + -v /path/to/data:/data \ + -v /path/to/docs:/docs \ + haiku-rag haiku-rag serve --mcp --monitor +``` + +Your `haiku.rag.yaml` must reference the **container path** for monitoring: + +```yaml +monitor: + directories: + - /docs # Container path, not host path +``` For API keys (OpenAI, Anthropic, etc.), pass them as environment variables: ```bash docker run -p 8001:8001 \ - -v $(pwd)/haiku.rag.yaml:/app/haiku.rag.yaml \ - -v $(pwd)/data:/data \ + -v /path/to/haiku.rag.yaml:/app/haiku.rag.yaml \ + -v /path/to/data:/data \ -e OPENAI_API_KEY=your-key-here \ haiku-rag ``` diff --git a/docs/installation.md b/docs/installation.md index 9d08444a..d665a54c 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -93,7 +93,10 @@ Build locally to include all features and document processing without docling-se ```bash docker build -f docker/Dockerfile -t haiku-rag . -docker run -p 8001:8001 -v $(pwd)/data:/data haiku-rag +docker run -p 8001:8001 \ + -v /path/to/haiku.rag.yaml:/app/haiku.rag.yaml \ + -v /path/to/data:/data \ + haiku-rag ``` -See `docker/README.md` for complete build and configuration instructions. +See `docker/README.md` for complete build and configuration instructions, including how to enable file monitoring. diff --git a/examples/docker/README.md b/examples/docker/README.md index 0f31b0f2..74f32661 100644 --- a/examples/docker/README.md +++ b/examples/docker/README.md @@ -14,13 +14,36 @@ This setup showcases the minimal haiku.rag-slim image combined with external doc ## Quick Start ```bash +# Create required directories mkdir -p data docs -cp haiku.rag.yaml.example haiku.rag.yaml # Edit as needed + +# Create config file from example (required) +cp haiku.rag.yaml.example haiku.rag.yaml + +# Start services docker compose up -d ``` Place documents in `docs/` for automatic indexing. +## Volume Mounts + +The docker-compose.yml mounts three volumes: + +| Host Path | Container Path | Purpose | +|-----------|---------------|---------| +| `./data` | `/data` | Persistent LanceDB database | +| `./docs` | `/docs` | Documents to monitor and index | +| `./haiku.rag.yaml` | `/app/haiku.rag.yaml` | Configuration file | + +**Important:** The `haiku.rag.yaml` config file must exist before running `docker compose up`. Copy it from the example: + +```bash +cp haiku.rag.yaml.example haiku.rag.yaml +``` + +The example config sets `monitor.directories: [/docs]` - this is the **container path**, not your host path. Documents placed in `./docs` on your host will appear at `/docs` inside the container. + ## Usage ```bash diff --git a/haiku_rag_slim/haiku/rag/monitor.py b/haiku_rag_slim/haiku/rag/monitor.py index c66949f2..45a1178e 100644 --- a/haiku_rag_slim/haiku/rag/monitor.py +++ b/haiku_rag_slim/haiku/rag/monitor.py @@ -90,6 +90,18 @@ class FileWatcher: self.supported_extensions = get_converter(config).supported_extensions async def observe(self): + if not self.paths: + logger.warning("No directories configured for monitoring") + return + + # Validate all paths exist before attempting to watch + missing_paths = [p for p in self.paths if not Path(p).exists()] + if missing_paths: + raise FileNotFoundError( + f"Monitor directories do not exist: {missing_paths}. " + "Check your haiku.rag.yaml configuration." + ) + logger.info(f"Watching files in {self.paths}") filter = FileFilter( ignore_patterns=self.ignore_patterns, diff --git a/tests/test_monitor.py b/tests/test_monitor.py index c9299b0b..78eeb5f6 100644 --- a/tests/test_monitor.py +++ b/tests/test_monitor.py @@ -440,3 +440,37 @@ async def test_file_watcher_orphan_handles_spaces_in_filenames(): # Should NOT delete the document since file exists mock_client.delete_document.assert_not_called() + + +@pytest.mark.asyncio +async def test_file_watcher_observe_raises_on_missing_paths(): + """Test observe() raises FileNotFoundError when directories don't exist.""" + mock_client = AsyncMock(spec=HaikuRAG) + + test_config = AppConfig( + monitor=MonitorConfig( + directories=[Path("/nonexistent/path/that/does/not/exist")] + ) + ) + watcher = FileWatcher(client=mock_client, config=test_config) + + with pytest.raises(FileNotFoundError) as exc_info: + await watcher.observe() + + assert "Monitor directories do not exist" in str(exc_info.value) + assert "haiku.rag.yaml" in str(exc_info.value) + + +@pytest.mark.asyncio +async def test_file_watcher_observe_returns_early_when_no_directories(): + """Test observe() returns early when no directories are configured.""" + mock_client = AsyncMock(spec=HaikuRAG) + + test_config = AppConfig(monitor=MonitorConfig(directories=[])) + watcher = FileWatcher(client=mock_client, config=test_config) + + # Should return without error when no directories configured + await watcher.observe() + + # No documents should have been processed + mock_client.create_document_from_source.assert_not_called()