Merge pull request #210 from ggozad/feat/tests-vcr
VCR Cassette Recording for API-less tests.
This commit is contained in:
commit
02f5dc4fb9
128 changed files with 45644 additions and 427 deletions
49
.github/workflows/test.yml
vendored
Normal file
49
.github/workflows/test.yml
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
name: Tests
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: astral-sh/setup-uv@v4
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version-file: "pyproject.toml"
|
||||
- name: Install dependencies
|
||||
run: uv sync --all-extras
|
||||
- name: Lint
|
||||
run: uv run ruff check
|
||||
- name: Type check
|
||||
run: uv run pyright
|
||||
|
||||
test:
|
||||
needs: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: astral-sh/setup-uv@v4
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version-file: "pyproject.toml"
|
||||
- name: Install dependencies
|
||||
run: uv sync --all-extras
|
||||
- name: Run tests with coverage
|
||||
run: uv run pytest -m "not integration" --cov=haiku --cov-report=xml
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./coverage.xml
|
||||
fail_ci_if_error: false
|
||||
12
CHANGELOG.md
12
CHANGELOG.md
|
|
@ -1,6 +1,18 @@
|
|||
# Changelog
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- **GitHub Actions CI**: Test workflow runs pytest, pyright, and ruff on push/PR to main
|
||||
- **VCR Cassette Recording**: Integration tests use recorded HTTP responses for deterministic CI runs
|
||||
- LLM tests (QA, embeddings, research graph) replay from cassettes without real API calls
|
||||
- docling-serve tests run without Docker container in CI
|
||||
- Uses pytest-recording with custom JSON body serializer
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed obsolete `test_config_validation_on_db_load` test (chunk_size changes don't invalidate database)
|
||||
|
||||
## [0.23.0] - 2025-12-26
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
# Haiku RAG
|
||||
|
||||
[](https://github.com/ggozad/haiku.rag/actions/workflows/test.yml)
|
||||
[](https://codecov.io/gh/ggozad/haiku.rag)
|
||||
|
||||
Agentic RAG built on [LanceDB](https://lancedb.com/), [Pydantic AI](https://ai.pydantic.dev/), and [Docling](https://docling-project.github.io/docling/).
|
||||
|
||||
## Features
|
||||
|
|
|
|||
116
docs/development.md
Normal file
116
docs/development.md
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# 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 pyright
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
When recording new cassettes, set real API keys via environment variables:
|
||||
|
||||
```bash
|
||||
ANTHROPIC_API_KEY=sk-ant-... uv run pytest tests/test_qa.py::test_qa_anthropic --record-mode=rewrite
|
||||
```
|
||||
|
|
@ -12,7 +12,7 @@ except ImportError as e:
|
|||
class CohereReranker(RerankerBase): # pragma: no cover
|
||||
def __init__(self):
|
||||
# Cohere SDK reads CO_API_KEY from environment by default
|
||||
self._client = cohere.ClientV2()
|
||||
self._client = cohere.AsyncClientV2()
|
||||
|
||||
async def rerank(
|
||||
self, query: str, chunks: list[Chunk], top_n: int = 10
|
||||
|
|
@ -23,7 +23,7 @@ class CohereReranker(RerankerBase): # pragma: no cover
|
|||
documents = [chunk.content for chunk in chunks]
|
||||
|
||||
model_name = self._model or "rerank-v3.5"
|
||||
response = self._client.rerank(
|
||||
response = await self._client.rerank(
|
||||
model=model_name, query=query, documents=documents, top_n=top_n
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from zeroentropy import ZeroEntropy
|
||||
from zeroentropy import AsyncZeroEntropy
|
||||
|
||||
from haiku.rag.reranking.base import RerankerBase
|
||||
from haiku.rag.store.models.chunk import Chunk
|
||||
|
|
@ -15,7 +15,7 @@ class ZeroEntropyReranker(RerankerBase): # pragma: no cover
|
|||
"""
|
||||
self._model = model
|
||||
# Zero Entropy SDK reads ZEROENTROPY_API_KEY from environment by default
|
||||
self._client = ZeroEntropy()
|
||||
self._client = AsyncZeroEntropy()
|
||||
|
||||
async def rerank(
|
||||
self, query: str, chunks: list[Chunk], top_n: int = 10
|
||||
|
|
@ -38,7 +38,7 @@ class ZeroEntropyReranker(RerankerBase): # pragma: no cover
|
|||
|
||||
# Call Zero Entropy reranking API
|
||||
model_name = self._model or "zerank-1"
|
||||
response = self._client.models.rerank(
|
||||
response = await self._client.models.rerank(
|
||||
model=model_name,
|
||||
query=query,
|
||||
documents=documents,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ nav:
|
|||
- MCP: mcp.md
|
||||
- Inspector: inspector.md
|
||||
- Benchmarks: benchmarks.md
|
||||
- Development: development.md
|
||||
- Changelog: changelog.md
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
|
|
|
|||
|
|
@ -71,10 +71,15 @@ dev = [
|
|||
"mkdocs>=1.6.1",
|
||||
"mkdocs-material>=9.7.0",
|
||||
"pre-commit>=4.5.0",
|
||||
"pydantic-ai-slim[anthropic]",
|
||||
"pydantic-ai-slim[bedrock]",
|
||||
"pydantic-ai-slim[google]",
|
||||
"pydantic-ai-slim[groq]",
|
||||
"pyright>=1.1.407",
|
||||
"pytest>=9.0.2",
|
||||
"pytest-asyncio>=1.3.0",
|
||||
"pytest-cov>=7.0.0",
|
||||
"pytest-recording>=0.13.2",
|
||||
"ruff>=0.14.8",
|
||||
]
|
||||
|
||||
|
|
|
|||
120
tests/cassettes/test_chunk/test_chunk_repository_operations.yaml
Normal file
120
tests/cassettes/test_chunk/test_chunk_repository_operations.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
80
tests/cassettes/test_chunk/test_chunking_pipeline.yaml
Normal file
80
tests/cassettes/test_chunk/test_chunking_pipeline.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
835
tests/cassettes/test_client/test_client_ask.yaml
Normal file
835
tests/cassettes/test_client/test_client_ask.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
120
tests/cassettes/test_client/test_client_document_crud.yaml
Normal file
120
tests/cassettes/test_client/test_client_document_crud.yaml
Normal file
File diff suppressed because one or more lines are too long
42
tests/cassettes/test_client/test_client_expand_context.yaml
Normal file
42
tests/cassettes/test_client/test_client_expand_context.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
242
tests/cassettes/test_client/test_client_search.yaml
Normal file
242
tests/cassettes/test_client/test_client_search.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
164
tests/cassettes/test_client/test_client_update_document.yaml
Normal file
164
tests/cassettes/test_client/test_client_update_document.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,100 @@
|
|||
interactions:
|
||||
- request:
|
||||
body: "--313fb28cff30a333a81f610f9c6bdaf3\r\nContent-Disposition: form-data; name=\"to_formats\"\r\n\r\njson\r\n--313fb28cff30a333a81f610f9c6bdaf3\r\nContent-Disposition:
|
||||
form-data; name=\"do_ocr\"\r\n\r\ntrue\r\n--313fb28cff30a333a81f610f9c6bdaf3\r\nContent-Disposition: form-data; name=\"force_ocr\"\r\n\r\nfalse\r\n--313fb28cff30a333a81f610f9c6bdaf3\r\nContent-Disposition:
|
||||
form-data; name=\"do_table_structure\"\r\n\r\ntrue\r\n--313fb28cff30a333a81f610f9c6bdaf3\r\nContent-Disposition: form-data;
|
||||
name=\"table_mode\"\r\n\r\naccurate\r\n--313fb28cff30a333a81f610f9c6bdaf3\r\nContent-Disposition: form-data; name=\"table_cell_matching\"\r\n\r\ntrue\r\n--313fb28cff30a333a81f610f9c6bdaf3\r\nContent-Disposition:
|
||||
form-data; name=\"images_scale\"\r\n\r\n2.0\r\n--313fb28cff30a333a81f610f9c6bdaf3\r\nContent-Disposition: form-data;
|
||||
name=\"generate_picture_images\"\r\n\r\nfalse\r\n--313fb28cff30a333a81f610f9c6bdaf3\r\nContent-Disposition: form-data;
|
||||
name=\"files\"; filename=\"tmpgc9za8mg.md\"\r\nContent-Type: text/markdown\r\n\r\n```python\ndef test():\n return
|
||||
42\n```\r\n--313fb28cff30a333a81f610f9c6bdaf3--\r\n"
|
||||
headers:
|
||||
accept:
|
||||
- '*/*'
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1011'
|
||||
content-type:
|
||||
- multipart/form-data; boundary=313fb28cff30a333a81f610f9c6bdaf3
|
||||
host:
|
||||
- localhost:5001
|
||||
method: POST
|
||||
uri: http://localhost:5001/v1/convert/file
|
||||
response:
|
||||
headers:
|
||||
content-length:
|
||||
- '1113'
|
||||
content-type:
|
||||
- application/json
|
||||
parsed_body:
|
||||
document:
|
||||
doctags_content: null
|
||||
filename: tmpgc9za8mg.md
|
||||
html_content: null
|
||||
json_content:
|
||||
body:
|
||||
children:
|
||||
- $ref: '#/texts/0'
|
||||
content_layer: body
|
||||
label: unspecified
|
||||
meta: null
|
||||
name: _root_
|
||||
parent: null
|
||||
self_ref: '#/body'
|
||||
form_items: []
|
||||
furniture:
|
||||
children: []
|
||||
content_layer: furniture
|
||||
label: unspecified
|
||||
meta: null
|
||||
name: _root_
|
||||
parent: null
|
||||
self_ref: '#/furniture'
|
||||
groups: []
|
||||
key_value_items: []
|
||||
name: tmpgc9za8mg
|
||||
origin:
|
||||
binary_hash: 9008975733065065710
|
||||
filename: tmpgc9za8mg.md
|
||||
mimetype: text/markdown
|
||||
uri: null
|
||||
pages: {}
|
||||
pictures: []
|
||||
schema_name: DoclingDocument
|
||||
tables: []
|
||||
texts:
|
||||
- captions: []
|
||||
children: []
|
||||
code_language: unknown
|
||||
content_layer: body
|
||||
footnotes: []
|
||||
formatting: null
|
||||
hyperlink: null
|
||||
image: null
|
||||
label: code
|
||||
meta: null
|
||||
orig: |-
|
||||
def test():
|
||||
return 42
|
||||
parent:
|
||||
$ref: '#/body'
|
||||
prov: []
|
||||
references: []
|
||||
self_ref: '#/texts/0'
|
||||
text: |-
|
||||
def test():
|
||||
return 42
|
||||
version: 1.8.0
|
||||
md_content: null
|
||||
text_content: null
|
||||
errors: []
|
||||
processing_time: 0.002258999999980915
|
||||
status: success
|
||||
timings: {}
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
interactions:
|
||||
- request:
|
||||
body: "--a906caa5bbe5d30e17b44fb63c456240\r\nContent-Disposition: form-data; name=\"to_formats\"\r\n\r\njson\r\n--a906caa5bbe5d30e17b44fb63c456240\r\nContent-Disposition:
|
||||
form-data; name=\"do_ocr\"\r\n\r\ntrue\r\n--a906caa5bbe5d30e17b44fb63c456240\r\nContent-Disposition: form-data; name=\"force_ocr\"\r\n\r\nfalse\r\n--a906caa5bbe5d30e17b44fb63c456240\r\nContent-Disposition:
|
||||
form-data; name=\"do_table_structure\"\r\n\r\ntrue\r\n--a906caa5bbe5d30e17b44fb63c456240\r\nContent-Disposition: form-data;
|
||||
name=\"table_mode\"\r\n\r\naccurate\r\n--a906caa5bbe5d30e17b44fb63c456240\r\nContent-Disposition: form-data; name=\"table_cell_matching\"\r\n\r\ntrue\r\n--a906caa5bbe5d30e17b44fb63c456240\r\nContent-Disposition:
|
||||
form-data; name=\"images_scale\"\r\n\r\n2.0\r\n--a906caa5bbe5d30e17b44fb63c456240\r\nContent-Disposition: form-data;
|
||||
name=\"generate_picture_images\"\r\n\r\nfalse\r\n--a906caa5bbe5d30e17b44fb63c456240\r\nContent-Disposition: form-data;
|
||||
name=\"files\"; filename=\"content.md\"\r\nContent-Type: text/markdown\r\n\r\n# Test Document\n\nThis is a test.\r\n--a906caa5bbe5d30e17b44fb63c456240--\r\n"
|
||||
headers:
|
||||
accept:
|
||||
- '*/*'
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1000'
|
||||
content-type:
|
||||
- multipart/form-data; boundary=a906caa5bbe5d30e17b44fb63c456240
|
||||
host:
|
||||
- localhost:5001
|
||||
method: POST
|
||||
uri: http://localhost:5001/v1/convert/file
|
||||
response:
|
||||
headers:
|
||||
content-length:
|
||||
- '1225'
|
||||
content-type:
|
||||
- application/json
|
||||
parsed_body:
|
||||
document:
|
||||
doctags_content: null
|
||||
filename: content.md
|
||||
html_content: null
|
||||
json_content:
|
||||
body:
|
||||
children:
|
||||
- $ref: '#/texts/0'
|
||||
- $ref: '#/texts/1'
|
||||
content_layer: body
|
||||
label: unspecified
|
||||
meta: null
|
||||
name: _root_
|
||||
parent: null
|
||||
self_ref: '#/body'
|
||||
form_items: []
|
||||
furniture:
|
||||
children: []
|
||||
content_layer: furniture
|
||||
label: unspecified
|
||||
meta: null
|
||||
name: _root_
|
||||
parent: null
|
||||
self_ref: '#/furniture'
|
||||
groups: []
|
||||
key_value_items: []
|
||||
name: content
|
||||
origin:
|
||||
binary_hash: 18149311266811077188
|
||||
filename: content.md
|
||||
mimetype: text/markdown
|
||||
uri: null
|
||||
pages: {}
|
||||
pictures: []
|
||||
schema_name: DoclingDocument
|
||||
tables: []
|
||||
texts:
|
||||
- children: []
|
||||
content_layer: body
|
||||
formatting: null
|
||||
hyperlink: null
|
||||
label: title
|
||||
meta: null
|
||||
orig: Test Document
|
||||
parent:
|
||||
$ref: '#/body'
|
||||
prov: []
|
||||
self_ref: '#/texts/0'
|
||||
text: Test Document
|
||||
- children: []
|
||||
content_layer: body
|
||||
formatting: null
|
||||
hyperlink: null
|
||||
label: text
|
||||
meta: null
|
||||
orig: This is a test.
|
||||
parent:
|
||||
$ref: '#/body'
|
||||
prov: []
|
||||
self_ref: '#/texts/1'
|
||||
text: This is a test.
|
||||
version: 1.8.0
|
||||
md_content: null
|
||||
text_content: null
|
||||
errors: []
|
||||
processing_time: 0.004331874999934371
|
||||
status: success
|
||||
timings: {}
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
File diff suppressed because one or more lines are too long
50
tests/cassettes/test_embedder/test_embed_chunks_basic.yaml
Normal file
50
tests/cassettes/test_embedder/test_embed_chunks_basic.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
170
tests/cassettes/test_embedder/test_ollama_embedder.yaml
Normal file
170
tests/cassettes/test_embedder/test_ollama_embedder.yaml
Normal file
File diff suppressed because one or more lines are too long
267
tests/cassettes/test_embedder/test_openai_embedder.yaml
Normal file
267
tests/cassettes/test_embedder/test_openai_embedder.yaml
Normal file
File diff suppressed because one or more lines are too long
150
tests/cassettes/test_embedder/test_voyageai_embedder.yaml
Normal file
150
tests/cassettes/test_embedder/test_voyageai_embedder.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
122
tests/cassettes/test_filter/test_search_with_title_filter.yaml
Normal file
122
tests/cassettes/test_filter/test_search_with_title_filter.yaml
Normal file
File diff suppressed because one or more lines are too long
162
tests/cassettes/test_filter/test_search_with_uri_filter.yaml
Normal file
162
tests/cassettes/test_filter/test_search_with_uri_filter.yaml
Normal file
File diff suppressed because one or more lines are too long
882
tests/cassettes/test_qa/test_qa_anthropic.yaml
Normal file
882
tests/cassettes/test_qa/test_qa_anthropic.yaml
Normal file
File diff suppressed because one or more lines are too long
1334
tests/cassettes/test_qa/test_qa_ollama.yaml
Normal file
1334
tests/cassettes/test_qa/test_qa_ollama.yaml
Normal file
File diff suppressed because one or more lines are too long
628
tests/cassettes/test_qa/test_qa_openai.yaml
Normal file
628
tests/cassettes/test_qa/test_qa_openai.yaml
Normal file
File diff suppressed because one or more lines are too long
158
tests/cassettes/test_rebuild/test_rebuild_embed_only.yaml
Normal file
158
tests/cassettes/test_rebuild/test_rebuild_embed_only.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
151
tests/cassettes/test_rebuild/test_rebuild_full.yaml
Normal file
151
tests/cassettes/test_rebuild/test_rebuild_full.yaml
Normal file
File diff suppressed because one or more lines are too long
151
tests/cassettes/test_rebuild/test_rebuild_rechunk.yaml
Normal file
151
tests/cassettes/test_rebuild/test_rebuild_rechunk.yaml
Normal file
File diff suppressed because one or more lines are too long
70
tests/cassettes/test_reranker/test_cohere_reranker.yaml
Normal file
70
tests/cassettes/test_reranker/test_cohere_reranker.yaml
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
interactions:
|
||||
- request:
|
||||
headers:
|
||||
accept:
|
||||
- '*/*'
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1261'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- api.cohere.com
|
||||
method: POST
|
||||
parsed_body:
|
||||
documents:
|
||||
- To Kill a Mockingbird is a novel by Harper Lee published in 1960. It was immediately successful, winning the Pulitzer
|
||||
Prize, and has become a classic of modern American literature.
|
||||
- The novel Moby-Dick was written by Herman Melville and first published in 1851. It is considered a masterpiece of
|
||||
American literature and deals with complex themes of obsession, revenge, and the conflict between good and evil.
|
||||
- Harper Lee, an American novelist widely known for her novel To Kill a Mockingbird, was born in 1926 in Monroeville,
|
||||
Alabama. She received the Pulitzer Prize for Fiction in 1961.
|
||||
- Jane Austen was an English novelist known primarily for her six major novels, which interpret, critique and comment
|
||||
upon the British landed gentry at the end of the 18th century.
|
||||
- The Harry Potter series, which consists of seven fantasy novels written by British author J.K. Rowling, is among the
|
||||
most popular and critically acclaimed books of the modern era.
|
||||
- The Great Gatsby, a novel written by American author F. Scott Fitzgerald, was published in 1925. The story is set
|
||||
in the Jazz Age and follows the life of millionaire Jay Gatsby and his pursuit of Daisy Buchanan.
|
||||
model: rerank-v3.5
|
||||
query: Who wrote 'To Kill a Mockingbird'?
|
||||
top_n: 2
|
||||
uri: https://api.cohere.com/v2/rerank
|
||||
response:
|
||||
headers:
|
||||
access-control-expose-headers:
|
||||
- X-Debug-Trace-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
|
||||
cache-control:
|
||||
- no-cache, no-store, no-transform, must-revalidate, private, max-age=0
|
||||
content-length:
|
||||
- '210'
|
||||
content-type:
|
||||
- application/json
|
||||
expires:
|
||||
- Thu, 01 Jan 1970 00:00:00 GMT
|
||||
pragma:
|
||||
- no-cache
|
||||
transfer-encoding:
|
||||
- chunked
|
||||
vary:
|
||||
- Origin,Accept-Encoding
|
||||
parsed_body:
|
||||
id: 56d8804a-d476-4c6a-ade8-69deb3fc8c64
|
||||
meta:
|
||||
api_version:
|
||||
version: '2'
|
||||
billed_units:
|
||||
search_units: 1
|
||||
results:
|
||||
- index: 0
|
||||
relevance_score: 0.9438932
|
||||
- index: 2
|
||||
relevance_score: 0.8455478
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
59
tests/cassettes/test_reranker/test_zeroentropy_reranker.yaml
Normal file
59
tests/cassettes/test_reranker/test_zeroentropy_reranker.yaml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
interactions:
|
||||
- request:
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1248'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- api.zeroentropy.dev
|
||||
method: POST
|
||||
parsed_body:
|
||||
documents:
|
||||
- To Kill a Mockingbird is a novel by Harper Lee published in 1960. It was immediately successful, winning the Pulitzer
|
||||
Prize, and has become a classic of modern American literature.
|
||||
- The novel Moby-Dick was written by Herman Melville and first published in 1851. It is considered a masterpiece of
|
||||
American literature and deals with complex themes of obsession, revenge, and the conflict between good and evil.
|
||||
- Harper Lee, an American novelist widely known for her novel To Kill a Mockingbird, was born in 1926 in Monroeville,
|
||||
Alabama. She received the Pulitzer Prize for Fiction in 1961.
|
||||
- Jane Austen was an English novelist known primarily for her six major novels, which interpret, critique and comment
|
||||
upon the British landed gentry at the end of the 18th century.
|
||||
- The Harry Potter series, which consists of seven fantasy novels written by British author J.K. Rowling, is among the
|
||||
most popular and critically acclaimed books of the modern era.
|
||||
- The Great Gatsby, a novel written by American author F. Scott Fitzgerald, was published in 1925. The story is set
|
||||
in the Jazz Age and follows the life of millionaire Jay Gatsby and his pursuit of Daisy Buchanan.
|
||||
model: zerank-1
|
||||
query: Who wrote 'To Kill a Mockingbird'?
|
||||
uri: https://api.zeroentropy.dev/v1/models/rerank
|
||||
response:
|
||||
headers:
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '309'
|
||||
content-type:
|
||||
- application/json
|
||||
parsed_body:
|
||||
results:
|
||||
- index: 2
|
||||
relevance_score: 0.9392035026199311
|
||||
- index: 0
|
||||
relevance_score: 0.918713920386427
|
||||
- index: 5
|
||||
relevance_score: 0.0860773208330919
|
||||
- index: 4
|
||||
relevance_score: 0.08035746882220708
|
||||
- index: 3
|
||||
relevance_score: 0.07673913563377131
|
||||
- index: 1
|
||||
relevance_score: 0.07498651627727318
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1629
tests/cassettes/test_search/test_search_qa_corpus.yaml
Normal file
1629
tests/cassettes/test_search/test_search_qa_corpus.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
242
tests/cassettes/test_search/test_search_score_types.yaml
Normal file
242
tests/cassettes/test_search/test_search_score_types.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,19 +1,34 @@
|
|||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
# Prevent tests from loading user's local haiku.rag.yaml by setting env var
|
||||
# to an empty config file BEFORE any haiku.rag imports.
|
||||
# This ensures tests always use default config values.
|
||||
# to a test config file BEFORE any haiku.rag imports.
|
||||
# Uses Ollama for embeddings - HTTP calls are recorded/replayed via VCR.
|
||||
_test_config_dir = tempfile.mkdtemp()
|
||||
_test_config_path = Path(_test_config_dir) / "test-defaults.yaml"
|
||||
_test_config_path.write_text("{}") # Empty YAML = use all defaults
|
||||
_test_config_path.write_text("""
|
||||
embeddings:
|
||||
model:
|
||||
provider: ollama
|
||||
name: qwen3-embedding:4b
|
||||
vector_dim: 2560
|
||||
""")
|
||||
os.environ["HAIKU_RAG_CONFIG_PATH"] = str(_test_config_path)
|
||||
|
||||
import pydantic_ai.models # noqa: E402
|
||||
import pytest # noqa: E402
|
||||
import yaml # noqa: E402
|
||||
from datasets import Dataset, load_dataset, load_from_disk # noqa: E402
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from vcr import VCR
|
||||
|
||||
pydantic_ai.models.ALLOW_MODEL_REQUESTS = False
|
||||
logging.getLogger("vcr.cassette").setLevel(logging.WARNING)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def qa_corpus() -> Dataset:
|
||||
|
|
@ -71,3 +86,46 @@ def temp_yaml_config(tmp_path, monkeypatch):
|
|||
monkeypatch.setenv("HAIKU_RAG_CONFIG_PATH", str(config_file))
|
||||
|
||||
yield config_file
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def allow_model_requests():
|
||||
with pydantic_ai.models.override_allow_model_requests(True):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def set_mock_api_keys(monkeypatch):
|
||||
"""Set mock API keys for providers that require them during initialization."""
|
||||
if not os.getenv("OPENAI_API_KEY"):
|
||||
monkeypatch.setenv("OPENAI_API_KEY", "sk-mock-key-for-vcr-playback")
|
||||
if not os.getenv("ANTHROPIC_API_KEY"):
|
||||
monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-ant-mock-key-for-vcr-playback")
|
||||
if not os.getenv("CO_API_KEY"):
|
||||
monkeypatch.setenv("CO_API_KEY", "mock-cohere-key-for-vcr-playback")
|
||||
if not os.getenv("ZEROENTROPY_API_KEY"):
|
||||
monkeypatch.setenv("ZEROENTROPY_API_KEY", "mock-ze-key-for-vcr-playback")
|
||||
if not os.getenv("VOYAGE_API_KEY"):
|
||||
monkeypatch.setenv("VOYAGE_API_KEY", "mock-voyage-key-for-vcr-playback")
|
||||
if not os.getenv("GROQ_API_KEY"):
|
||||
monkeypatch.setenv("GROQ_API_KEY", "mock-groq-key-for-vcr-playback")
|
||||
if not os.getenv("GOOGLE_API_KEY"):
|
||||
monkeypatch.setenv("GOOGLE_API_KEY", "mock-google-key-for-vcr-playback")
|
||||
if not os.getenv("AWS_DEFAULT_REGION"):
|
||||
monkeypatch.setenv("AWS_DEFAULT_REGION", "us-east-1")
|
||||
|
||||
|
||||
def pytest_recording_configure(config: Any, vcr: "VCR"):
|
||||
from . import json_body_serializer
|
||||
|
||||
vcr.register_serializer("yaml", json_body_serializer)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vcr_config():
|
||||
return {
|
||||
"ignore_localhost": False,
|
||||
"ignore_hosts": ["huggingface.co"],
|
||||
"filter_headers": ["authorization", "x-api-key"],
|
||||
"decode_compressed_response": True,
|
||||
}
|
||||
|
|
|
|||
BIN
tests/data/dataset/data-00000-of-00001.arrow
Normal file
BIN
tests/data/dataset/data-00000-of-00001.arrow
Normal file
Binary file not shown.
106
tests/data/dataset/dataset_info.json
Normal file
106
tests/data/dataset/dataset_info.json
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
{
|
||||
"builder_name": "parquet",
|
||||
"citation": "",
|
||||
"config_name": "default",
|
||||
"dataset_name": "repliqa",
|
||||
"dataset_size": 648387648,
|
||||
"description": "",
|
||||
"download_checksums": {
|
||||
"hf://datasets/ServiceNow/repliqa@bc880adc948fd3a70d5f8b2b3a1d1ee90d820dbd/data/repliqa_0-00000-of-00001.parquet": {
|
||||
"num_bytes": 20855947,
|
||||
"checksum": null
|
||||
},
|
||||
"hf://datasets/ServiceNow/repliqa@bc880adc948fd3a70d5f8b2b3a1d1ee90d820dbd/data/repliqa_1-00000-of-00001.parquet": {
|
||||
"num_bytes": 20903799,
|
||||
"checksum": null
|
||||
},
|
||||
"hf://datasets/ServiceNow/repliqa@bc880adc948fd3a70d5f8b2b3a1d1ee90d820dbd/data/repliqa_2-00000-of-00001.parquet": {
|
||||
"num_bytes": 20876034,
|
||||
"checksum": null
|
||||
},
|
||||
"hf://datasets/ServiceNow/repliqa@bc880adc948fd3a70d5f8b2b3a1d1ee90d820dbd/data/repliqa_3-00000-of-00001.parquet": {
|
||||
"num_bytes": 20919214,
|
||||
"checksum": null
|
||||
},
|
||||
"hf://datasets/ServiceNow/repliqa@bc880adc948fd3a70d5f8b2b3a1d1ee90d820dbd/data/repliqa_4-00000-of-00001.parquet": {
|
||||
"num_bytes": 20866527,
|
||||
"checksum": null
|
||||
}
|
||||
},
|
||||
"download_size": 104421521,
|
||||
"features": {
|
||||
"document_id": {
|
||||
"dtype": "string",
|
||||
"_type": "Value"
|
||||
},
|
||||
"document_topic": {
|
||||
"dtype": "string",
|
||||
"_type": "Value"
|
||||
},
|
||||
"document_path": {
|
||||
"dtype": "string",
|
||||
"_type": "Value"
|
||||
},
|
||||
"document_extracted": {
|
||||
"dtype": "string",
|
||||
"_type": "Value"
|
||||
},
|
||||
"question_id": {
|
||||
"dtype": "string",
|
||||
"_type": "Value"
|
||||
},
|
||||
"question": {
|
||||
"dtype": "string",
|
||||
"_type": "Value"
|
||||
},
|
||||
"answer": {
|
||||
"dtype": "string",
|
||||
"_type": "Value"
|
||||
},
|
||||
"long_answer": {
|
||||
"dtype": "string",
|
||||
"_type": "Value"
|
||||
}
|
||||
},
|
||||
"homepage": "",
|
||||
"license": "",
|
||||
"size_in_bytes": 752809169,
|
||||
"splits": {
|
||||
"repliqa_0": {
|
||||
"name": "repliqa_0",
|
||||
"num_bytes": 129579219,
|
||||
"num_examples": 17955,
|
||||
"dataset_name": "repliqa"
|
||||
},
|
||||
"repliqa_1": {
|
||||
"name": "repliqa_1",
|
||||
"num_bytes": 129844840,
|
||||
"num_examples": 17955,
|
||||
"dataset_name": "repliqa"
|
||||
},
|
||||
"repliqa_2": {
|
||||
"name": "repliqa_2",
|
||||
"num_bytes": 129338522,
|
||||
"num_examples": 17955,
|
||||
"dataset_name": "repliqa"
|
||||
},
|
||||
"repliqa_3": {
|
||||
"name": "repliqa_3",
|
||||
"num_bytes": 129981017,
|
||||
"num_examples": 17955,
|
||||
"dataset_name": "repliqa"
|
||||
},
|
||||
"repliqa_4": {
|
||||
"name": "repliqa_4",
|
||||
"num_bytes": 129644050,
|
||||
"num_examples": 17950,
|
||||
"dataset_name": "repliqa"
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"version_str": "0.0.0",
|
||||
"major": 0,
|
||||
"minor": 0,
|
||||
"patch": 0
|
||||
}
|
||||
}
|
||||
13
tests/data/dataset/state.json
Normal file
13
tests/data/dataset/state.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"_data_files": [
|
||||
{
|
||||
"filename": "data-00000-of-00001.arrow"
|
||||
}
|
||||
],
|
||||
"_fingerprint": "2e29f63d782f12f0",
|
||||
"_format_columns": null,
|
||||
"_format_kwargs": {},
|
||||
"_format_type": null,
|
||||
"_output_all_columns": false,
|
||||
"_split": "repliqa_3"
|
||||
}
|
||||
3509
tests/graph/cassettes/test_research_graph/test_graph_end_to_end.yaml
Normal file
3509
tests/graph/cassettes/test_research_graph/test_graph_end_to_end.yaml
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue