Move search related config under SearchConfig
This commit is contained in:
parent
4da50873bd
commit
802f058205
8 changed files with 49 additions and 46 deletions
|
|
@ -52,7 +52,11 @@
|
||||||
- `content` and `docling_document` are mutually exclusive
|
- `content` and `docling_document` are mutually exclusive
|
||||||
- **BREAKING: Chunker Interface**: `DocumentChunker.chunk()` now returns `list[Chunk]` instead of `list[str]`
|
- **BREAKING: Chunker Interface**: `DocumentChunker.chunk()` now returns `list[Chunk]` instead of `list[str]`
|
||||||
- Chunks include structured metadata (doc_item_refs, labels, headings, page_numbers)
|
- Chunks include structured metadata (doc_item_refs, labels, headings, page_numbers)
|
||||||
- **BREAKING: Config Renamed**: `context_chunk_radius` renamed to `text_context_radius`
|
- **Search Config**: New settings in `search` section for search behavior and context expansion
|
||||||
|
- `search.limit` - Default number of search results (default: 5). Used by CLI, MCP server, and API when no limit specified
|
||||||
|
- `search.context_radius` - DocItems before/after to include for text content expansion (default: 0)
|
||||||
|
- `search.max_context_items` - Maximum items in expanded context (default: 25)
|
||||||
|
- `search.max_context_chars` - Maximum characters in expanded context (default: 10000)
|
||||||
- **Rebuild Performance**: Batched database writes during `rebuild` command reduce LanceDB versions by ~98%
|
- **Rebuild Performance**: Batched database writes during `rebuild` command reduce LanceDB versions by ~98%
|
||||||
- All rebuild modes (FULL, RECHUNK, EMBED_ONLY) now batch writes across documents
|
- All rebuild modes (FULL, RECHUNK, EMBED_ONLY) now batch writes across documents
|
||||||
- Eliminates redundant per-document chunk deletions and vacuum calls
|
- Eliminates redundant per-document chunk deletions and vacuum calls
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,9 @@ research:
|
||||||
|
|
||||||
search:
|
search:
|
||||||
limit: 5 # Default number of results to return
|
limit: 5 # Default number of results to return
|
||||||
|
context_radius: 0 # DocItems before/after to include for text content
|
||||||
|
max_context_items: 25 # Maximum items in expanded context
|
||||||
|
max_context_chars: 10000 # Maximum characters in expanded context
|
||||||
vector_index_metric: cosine # cosine, l2, or dot
|
vector_index_metric: cosine # cosine, l2, or dot
|
||||||
vector_refine_factor: 30
|
vector_refine_factor: 30
|
||||||
|
|
||||||
|
|
@ -116,9 +119,6 @@ processing:
|
||||||
chunker: docling-local # docling-local or docling-serve
|
chunker: docling-local # docling-local or docling-serve
|
||||||
chunker_type: hybrid # hybrid or hierarchical
|
chunker_type: hybrid # hybrid or hierarchical
|
||||||
chunk_size: 256
|
chunk_size: 256
|
||||||
text_context_radius: 0
|
|
||||||
max_context_items: 25
|
|
||||||
max_context_chars: 10000
|
|
||||||
chunking_tokenizer: "Qwen/Qwen3-Embedding-0.6B"
|
chunking_tokenizer: "Qwen/Qwen3-Embedding-0.6B"
|
||||||
chunking_merge_peers: true
|
chunking_merge_peers: true
|
||||||
chunking_use_markdown_tables: false
|
chunking_use_markdown_tables: false
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,6 @@ processing:
|
||||||
# Chunking configuration
|
# Chunking configuration
|
||||||
chunk_size: 256 # Maximum tokens per chunk
|
chunk_size: 256 # Maximum tokens per chunk
|
||||||
|
|
||||||
# Context expansion for search results
|
|
||||||
text_context_radius: 0 # Radius for text chunk expansion
|
|
||||||
max_context_items: 25 # Maximum items in expanded context
|
|
||||||
max_context_chars: 10000 # Maximum characters in expanded context
|
|
||||||
|
|
||||||
# Converter selection
|
# Converter selection
|
||||||
converter: docling-local # docling-local or docling-serve
|
converter: docling-local # docling-local or docling-serve
|
||||||
|
|
||||||
|
|
@ -137,27 +132,14 @@ processing:
|
||||||
- `false`: Tables as narrative text ("Value A, Column 2 = Value B")
|
- `false`: Tables as narrative text ("Value A, Column 2 = Value B")
|
||||||
- `true`: Tables as markdown (preserves table structure)
|
- `true`: Tables as markdown (preserves table structure)
|
||||||
|
|
||||||
### Chunk Size and Context Expansion
|
### Chunk Size
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
processing:
|
processing:
|
||||||
# Chunk size for document processing
|
chunk_size: 256 # Maximum tokens per chunk
|
||||||
chunk_size: 256
|
|
||||||
|
|
||||||
# Context expansion settings
|
|
||||||
# Controls how search results are expanded with surrounding content
|
|
||||||
text_context_radius: 0 # Chunks before/after to include for text content
|
|
||||||
max_context_items: 25 # Maximum doc items to include in expansion
|
|
||||||
max_context_chars: 10000 # Maximum characters in expanded content
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Context expansion enriches search results with surrounding content from the source document:
|
Context expansion settings (for enriching search results with surrounding content) are configured in the `search` section. See [Search Settings](storage.md#search-settings).
|
||||||
|
|
||||||
- **text_context_radius**: For text content (paragraphs), includes N chunks before and after. Set to 0 to disable expansion (default).
|
|
||||||
- **max_context_items**: Limits how many document items (paragraphs, list items, etc.) can be included in expanded context.
|
|
||||||
- **max_context_chars**: Hard limit on total characters in expanded content.
|
|
||||||
|
|
||||||
Structural content (tables, code blocks, lists) uses type-aware expansion that automatically includes the complete structure regardless of how it was chunked. For example, if a table was split across multiple chunks, expansion retrieves the complete table.
|
|
||||||
|
|
||||||
## File Monitoring
|
## File Monitoring
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,16 +59,32 @@ This prevents the common mistake where a search query accidentally creates an em
|
||||||
|
|
||||||
## Search Settings
|
## Search Settings
|
||||||
|
|
||||||
Configure search behavior:
|
Configure search behavior and context expansion:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
search:
|
search:
|
||||||
limit: 5 # Default number of results to return
|
limit: 5 # Default number of results to return
|
||||||
|
context_radius: 0 # DocItems before/after to include for text content
|
||||||
|
max_context_items: 25 # Maximum items in expanded context
|
||||||
|
max_context_chars: 10000 # Maximum characters in expanded context
|
||||||
vector_index_metric: cosine # cosine, l2, or dot
|
vector_index_metric: cosine # cosine, l2, or dot
|
||||||
vector_refine_factor: 30 # Re-ranking factor for accuracy
|
vector_refine_factor: 30 # Re-ranking factor for accuracy
|
||||||
```
|
```
|
||||||
|
|
||||||
- **limit**: Default number of search results to return when no limit is specified. Used by CLI, MCP server, and API. Default: 5
|
- **limit**: Default number of search results to return when no limit is specified. Used by CLI, MCP server, and API. Default: 5
|
||||||
|
|
||||||
|
### Context Expansion
|
||||||
|
|
||||||
|
Context expansion enriches search results with surrounding content from the source document:
|
||||||
|
|
||||||
|
- **context_radius**: For text content (paragraphs), includes N DocItems before and after. Set to 0 to disable expansion (default).
|
||||||
|
- **max_context_items**: Limits how many document items (paragraphs, list items, etc.) can be included in expanded context.
|
||||||
|
- **max_context_chars**: Hard limit on total characters in expanded content.
|
||||||
|
|
||||||
|
Structural content (tables, code blocks, lists) uses type-aware expansion that automatically includes the complete structure regardless of how it was chunked. For example, if a table was split across multiple chunks, expansion retrieves the complete table.
|
||||||
|
|
||||||
|
### Vector Indexing
|
||||||
|
|
||||||
- **vector_index_metric**: Distance metric for vector similarity:
|
- **vector_index_metric**: Distance metric for vector similarity:
|
||||||
- `cosine`: Cosine similarity (default, best for most embeddings)
|
- `cosine`: Cosine similarity (default, best for most embeddings)
|
||||||
- `l2`: Euclidean distance
|
- `l2`: Euclidean distance
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,10 @@ def build_experiment_metadata(
|
||||||
"embedder_model": config.embeddings.model.name,
|
"embedder_model": config.embeddings.model.name,
|
||||||
"embedder_dim": config.embeddings.model.vector_dim,
|
"embedder_dim": config.embeddings.model.vector_dim,
|
||||||
"chunk_size": config.processing.chunk_size,
|
"chunk_size": config.processing.chunk_size,
|
||||||
"text_context_radius": config.processing.text_context_radius,
|
"search_limit": config.search.limit,
|
||||||
"max_context_items": config.processing.max_context_items,
|
"context_radius": config.search.context_radius,
|
||||||
"max_context_chars": config.processing.max_context_chars,
|
"max_context_items": config.search.max_context_items,
|
||||||
|
"max_context_chars": config.search.max_context_chars,
|
||||||
"rerank_provider": config.reranking.model.provider
|
"rerank_provider": config.reranking.model.provider
|
||||||
if config.reranking.model
|
if config.reranking.model
|
||||||
else None,
|
else None,
|
||||||
|
|
|
||||||
|
|
@ -880,8 +880,8 @@ class HaikuRAG:
|
||||||
|
|
||||||
Expansion is type-aware based on content:
|
Expansion is type-aware based on content:
|
||||||
- Tables, code blocks, and lists expand to include complete structures
|
- Tables, code blocks, and lists expand to include complete structures
|
||||||
- Text content uses the configured radius (text_context_radius)
|
- Text content uses the configured radius (search.context_radius)
|
||||||
- Expansion is limited by max_context_items and max_context_chars
|
- Expansion is limited by search.max_context_items and search.max_context_chars
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
search_results: List of SearchResult objects from search.
|
search_results: List of SearchResult objects from search.
|
||||||
|
|
@ -889,9 +889,9 @@ class HaikuRAG:
|
||||||
Returns:
|
Returns:
|
||||||
List of SearchResult objects with expanded content and resolved provenance.
|
List of SearchResult objects with expanded content and resolved provenance.
|
||||||
"""
|
"""
|
||||||
radius = self._config.processing.text_context_radius
|
radius = self._config.search.context_radius
|
||||||
max_items = self._config.processing.max_context_items
|
max_items = self._config.search.max_context_items
|
||||||
max_chars = self._config.processing.max_context_chars
|
max_chars = self._config.search.max_context_chars
|
||||||
|
|
||||||
# Group by document_id for efficient processing
|
# Group by document_id for efficient processing
|
||||||
document_groups: dict[str | None, list[SearchResult]] = {}
|
document_groups: dict[str | None, list[SearchResult]] = {}
|
||||||
|
|
|
||||||
|
|
@ -110,9 +110,6 @@ class ConversionOptions(BaseModel):
|
||||||
|
|
||||||
class ProcessingConfig(BaseModel):
|
class ProcessingConfig(BaseModel):
|
||||||
chunk_size: int = 256
|
chunk_size: int = 256
|
||||||
text_context_radius: int = 0
|
|
||||||
max_context_items: int = 25
|
|
||||||
max_context_chars: int = 10000
|
|
||||||
converter: str = "docling-local"
|
converter: str = "docling-local"
|
||||||
chunker: str = "docling-local"
|
chunker: str = "docling-local"
|
||||||
chunker_type: str = "hybrid"
|
chunker_type: str = "hybrid"
|
||||||
|
|
@ -124,6 +121,9 @@ class ProcessingConfig(BaseModel):
|
||||||
|
|
||||||
class SearchConfig(BaseModel):
|
class SearchConfig(BaseModel):
|
||||||
limit: int = 5
|
limit: int = 5
|
||||||
|
context_radius: int = 0
|
||||||
|
max_context_items: int = 25
|
||||||
|
max_context_chars: int = 10000
|
||||||
vector_index_metric: Literal["cosine", "l2", "dot"] = "cosine"
|
vector_index_metric: Literal["cosine", "l2", "dot"] = "cosine"
|
||||||
vector_refine_factor: int = 30
|
vector_refine_factor: int = 30
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,8 @@ def small_chunk_config() -> AppConfig:
|
||||||
"""Config with small chunk size to force splitting."""
|
"""Config with small chunk size to force splitting."""
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
config.processing.chunk_size = 32
|
config.processing.chunk_size = 32
|
||||||
config.processing.max_context_items = 25
|
config.search.max_context_items = 25
|
||||||
config.processing.max_context_chars = 10000
|
config.search.max_context_chars = 10000
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -228,7 +228,7 @@ async def test_text_expansion_uses_radius(temp_db_path):
|
||||||
"""Text content expansion should use radius, not structural boundaries."""
|
"""Text content expansion should use radius, not structural boundaries."""
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
config.processing.chunk_size = 32
|
config.processing.chunk_size = 32
|
||||||
config.processing.text_context_radius = 1 # Small radius
|
config.search.context_radius = 1 # Small radius
|
||||||
|
|
||||||
# Create a document with longer paragraphs that will split
|
# Create a document with longer paragraphs that will split
|
||||||
doc = DoclingDocument(name="text_test")
|
doc = DoclingDocument(name="text_test")
|
||||||
|
|
@ -312,7 +312,7 @@ async def test_max_items_limit_caps_expansion(temp_db_path):
|
||||||
"""Expansion should respect max_context_items limit."""
|
"""Expansion should respect max_context_items limit."""
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
config.processing.chunk_size = 32
|
config.processing.chunk_size = 32
|
||||||
config.processing.max_context_items = 2 # Very restrictive
|
config.search.max_context_items = 2 # Very restrictive
|
||||||
|
|
||||||
docling_doc = create_list_document()
|
docling_doc = create_list_document()
|
||||||
|
|
||||||
|
|
@ -410,7 +410,7 @@ async def test_expand_context_radius_zero(temp_db_path):
|
||||||
async def test_expand_context_multiple_documents(temp_db_path):
|
async def test_expand_context_multiple_documents(temp_db_path):
|
||||||
"""Test expand_context with results from multiple documents."""
|
"""Test expand_context with results from multiple documents."""
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
config.processing.text_context_radius = 1
|
config.search.context_radius = 1
|
||||||
|
|
||||||
async with HaikuRAG(temp_db_path, config=config, create=True) as client:
|
async with HaikuRAG(temp_db_path, config=config, create=True) as client:
|
||||||
# Create first document with manual chunks
|
# Create first document with manual chunks
|
||||||
|
|
@ -471,7 +471,7 @@ async def test_expand_context_multiple_documents(temp_db_path):
|
||||||
async def test_expand_context_merges_overlapping_chunks(temp_db_path):
|
async def test_expand_context_merges_overlapping_chunks(temp_db_path):
|
||||||
"""Test that overlapping expanded chunks are merged into one."""
|
"""Test that overlapping expanded chunks are merged into one."""
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
config.processing.text_context_radius = 1
|
config.search.context_radius = 1
|
||||||
|
|
||||||
async with HaikuRAG(temp_db_path, config=config, create=True) as client:
|
async with HaikuRAG(temp_db_path, config=config, create=True) as client:
|
||||||
# Create document with 5 chunks
|
# Create document with 5 chunks
|
||||||
|
|
@ -526,7 +526,7 @@ async def test_expand_context_merges_overlapping_chunks(temp_db_path):
|
||||||
async def test_expand_context_keeps_separate_non_overlapping(temp_db_path):
|
async def test_expand_context_keeps_separate_non_overlapping(temp_db_path):
|
||||||
"""Test that non-overlapping expanded chunks remain separate."""
|
"""Test that non-overlapping expanded chunks remain separate."""
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
config.processing.text_context_radius = 1
|
config.search.context_radius = 1
|
||||||
|
|
||||||
async with HaikuRAG(temp_db_path, config=config, create=True) as client:
|
async with HaikuRAG(temp_db_path, config=config, create=True) as client:
|
||||||
# Create document with chunks far apart
|
# Create document with chunks far apart
|
||||||
|
|
@ -591,7 +591,7 @@ async def test_expand_context_keeps_separate_non_overlapping(temp_db_path):
|
||||||
async def test_expand_context_with_docling_merges_overlapping(temp_db_path):
|
async def test_expand_context_with_docling_merges_overlapping(temp_db_path):
|
||||||
"""Test that expand_context with DoclingDocument merges overlapping results."""
|
"""Test that expand_context with DoclingDocument merges overlapping results."""
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
config.processing.text_context_radius = 3
|
config.search.context_radius = 3
|
||||||
|
|
||||||
markdown_content = """# Chapter 1
|
markdown_content = """# Chapter 1
|
||||||
|
|
||||||
|
|
@ -646,7 +646,7 @@ This is paragraph four about topic C.
|
||||||
async def test_expand_context_docling_merges_metadata(temp_db_path):
|
async def test_expand_context_docling_merges_metadata(temp_db_path):
|
||||||
"""Test that expand_context properly merges metadata from multiple results."""
|
"""Test that expand_context properly merges metadata from multiple results."""
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
config.processing.text_context_radius = 10
|
config.search.context_radius = 10
|
||||||
|
|
||||||
markdown_content = """# Introduction
|
markdown_content = """# Introduction
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue