Sixty-three comments said what the next statement already said: # Connect to LanceDB above connect_lancedb, # Path object above isinstance(source, Path), # Get page numbers from provenance above the prov loop, # Clear and populate results above list_view.clear(). They cost a read and carry nothing. The line is whether a comment restates one statement or labels a phase. Phase labels stay: the migrations keep # Create staging table with new schema and # Copy from staging to final table in batches, each heading ten lines of a long procedure. So do comments carrying a fact the code cannot: the merge_insert update-only note on document_meta, why the poller builds sources eagerly, why create_document_from_source returns a list for directories, that indexes need training data, the field-group markers in the config models, and the file:// URL-encoding note in create_document_from_source. capabilities/ is untouched. Its docstrings sit next to prompt surface, and changing them needs an eval to back it. The cassette-recording docs were wrong three ways. They named tests/test_qa.py::test_qa_anthropic, which no longer exists; they targeted whole modules, so a rewrite would re-record cassettes for services the recorder is not running; and they used COHERE_API_KEY where the SDK reads CO_API_KEY. docs/development.md now names exact tests with -n0, and the keyed example is test_cohere_reranker, which owns the one cassette recording api.cohere.com.
123 lines
3.5 KiB
Markdown
123 lines
3.5 KiB
Markdown
# Development
|
|
|
|
This guide covers setting up a development environment and running tests.
|
|
|
|
## Setup
|
|
|
|
Clone the repository and install dependencies:
|
|
|
|
```bash
|
|
git clone https://github.com/ggozad/haiku.rag.git
|
|
cd haiku.rag
|
|
uv sync
|
|
```
|
|
|
|
## Running Tests
|
|
|
|
```bash
|
|
uv run pytest
|
|
```
|
|
|
|
### Test Markers
|
|
|
|
Tests use pytest markers to categorize them:
|
|
|
|
- `@pytest.mark.integration` - Tests requiring local services (Docling models, etc.) that aren't available in CI
|
|
- `@pytest.mark.asyncio` - Async tests (applied automatically via pytest-asyncio)
|
|
- `@pytest.mark.vcr()` - Tests with HTTP call recording
|
|
|
|
CI runs `pytest -m "not integration"` to skip integration tests.
|
|
|
|
## HTTP Recording with VCR
|
|
|
|
Tests use [pytest-recording](https://github.com/kiwicom/pytest-recording) (VCR.py) to record and replay HTTP calls. This allows tests to run without external services like Ollama or API providers.
|
|
|
|
### How It Works
|
|
|
|
1. Tests marked with `@pytest.mark.vcr()` record HTTP interactions to YAML cassettes
|
|
2. On subsequent runs, HTTP calls are replayed from cassettes instead of hitting real services
|
|
3. Cassettes are committed to the repository so CI can run tests without external dependencies
|
|
|
|
### Recording New Cassettes
|
|
|
|
When adding a new test that makes HTTP calls:
|
|
|
|
1. Add the `@pytest.mark.vcr()` decorator to your test
|
|
2. Run the test with the required services available (e.g., Ollama running)
|
|
3. The cassette is automatically created on first run
|
|
|
|
### Re-recording Cassettes
|
|
|
|
To update an existing cassette, delete it and re-run the test, or use `--record-mode=rewrite`.
|
|
|
|
### Running Without Cassettes (Live Mode)
|
|
|
|
To run tests against real services instead of recorded cassettes:
|
|
|
|
```bash
|
|
uv run pytest --disable-recording
|
|
```
|
|
|
|
## Writing Tests
|
|
|
|
### Common Fixtures
|
|
|
|
Available fixtures from `tests/conftest.py`:
|
|
|
|
- `temp_db_path` - Isolated temporary database
|
|
- `temp_yaml_config` - Temporary config file
|
|
- `allow_model_requests` - Enables pydantic-ai model calls
|
|
|
|
### Example: Adding a New Test with VCR
|
|
|
|
```python
|
|
import pytest
|
|
from haiku.rag.client import HaikuRAG
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.vcr()
|
|
async def test_my_feature(temp_db_path):
|
|
async with HaikuRAG(temp_db_path, create=True) as client:
|
|
doc = await client.create_document("Test content", uri="test://doc")
|
|
assert doc.id is not None
|
|
```
|
|
|
|
### Integration Tests
|
|
|
|
For tests requiring local services that can't be mocked via VCR:
|
|
|
|
```python
|
|
@pytest.mark.integration
|
|
@pytest.mark.asyncio
|
|
async def test_pdf_visualization(temp_db_path):
|
|
# Test code that needs local PDF processing
|
|
pass
|
|
```
|
|
|
|
Integration tests are skipped in CI but run locally when you have the required services.
|
|
|
|
## Linting and Formatting
|
|
|
|
```bash
|
|
uv run ruff check
|
|
uv run ruff format
|
|
uv run ty check
|
|
```
|
|
|
|
## Mock API Keys
|
|
|
|
Tests automatically set mock API keys for providers that require them during client initialization. When running with VCR playback, these mock keys are sufficient since no real API calls are made.
|
|
|
|
Recording reaches the real service, so the recording command needs network
|
|
access and the keys that service reads. Name the exact test and pass `-n0`:
|
|
a module-wide `--record-mode=rewrite` re-records every cassette in it,
|
|
including ones whose service you do not have running.
|
|
|
|
```bash
|
|
# Ollama-backed cassettes need no key, only a running Ollama
|
|
uv run pytest tests/test_embedder.py::test_ollama_embedder -n0 --record-mode=rewrite
|
|
|
|
# A keyed provider reads its own variable. Cohere's SDK reads CO_API_KEY
|
|
CO_API_KEY=... uv run pytest tests/test_reranker.py::test_cohere_reranker -n0 --record-mode=rewrite
|
|
```
|