fix skill analyze tool ignores state.document_filter

This commit is contained in:
Yiorgis Gozadinos 2026-04-09 11:08:57 +03:00
parent 99af4fe11d
commit a51b922be3
No known key found for this signature in database
5 changed files with 58 additions and 5 deletions

View file

@ -9,6 +9,7 @@
### Fixed ### Fixed
- **Skill `list_documents` ignores `document_filter`**: `list_documents` tool now respects `state.document_filter`, consistent with `search`, `ask`, and `research` - **Skill `list_documents` ignores `document_filter`**: `list_documents` tool now respects `state.document_filter`, consistent with `search`, `ask`, and `research`
- **Skill `analyze` ignores `document_filter`**: `analyze` tool now uses `state.document_filter` (combined with any explicit `filter` parameter). Added `document_filter` field to `RLMState`
## [0.38.0] - 2026-04-07 ## [0.38.0] - 2026-04-07

View file

@ -33,6 +33,7 @@ The skill manages an `RLMState` under the `"rlm"` namespace:
```python ```python
class RLMState(BaseModel): class RLMState(BaseModel):
document_filter: str | None = None
analyses: list[AnalysisEntry] = [] analyses: list[AnalysisEntry] = []
class AnalysisEntry(BaseModel): class AnalysisEntry(BaseModel):
@ -41,7 +42,8 @@ class AnalysisEntry(BaseModel):
program: str | None = None program: str | None = None
``` ```
Each `analyze` call appends an `AnalysisEntry` with the question, answer, and executed program. - **document_filter** — SQL WHERE clause applied to `analyze` calls (combined with any explicit `filter` parameter). Set this to scope analysis to specific documents.
- **analyses** — Each `analyze` call appends an `AnalysisEntry` with the question, answer, and executed program.
## Usage with RAG Skill ## Usage with RAG Skill

View file

@ -8,6 +8,7 @@ from haiku.rag.agents.research.models import Citation
from haiku.rag.config.models import AppConfig from haiku.rag.config.models import AppConfig
from haiku.rag.store.models.chunk import SearchResult from haiku.rag.store.models.chunk import SearchResult
from haiku.rag.tools.document import DocumentInfo from haiku.rag.tools.document import DocumentInfo
from haiku.rag.tools.filters import combine_filters
from haiku.rag.tools.qa import QAHistoryEntry from haiku.rag.tools.qa import QAHistoryEntry
from haiku.skills.state import SkillRunDeps from haiku.skills.state import SkillRunDeps
@ -476,11 +477,12 @@ def create_skill_tools(
document: Optional document ID or title to pre-load for analysis. document: Optional document ID or title to pre-load for analysis.
filter: Optional SQL WHERE clause to filter documents. filter: Optional SQL WHERE clause to filter documents.
""" """
output, answer, program = await skill_analyze(
db_path, config, question, document=document, filter=filter
)
state = _get_state(ctx, state_type) state = _get_state(ctx, state_type)
state_filter = state.document_filter if state else None
effective_filter = combine_filters(state_filter, filter)
output, answer, program = await skill_analyze(
db_path, config, question, document=document, filter=effective_filter
)
if state: if state:
state.analyses.append( state.analyses.append(
AnalysisEntry( AnalysisEntry(

View file

@ -11,6 +11,7 @@ from haiku.skills.parser import parse_skill_md
class RLMState(BaseModel): class RLMState(BaseModel):
document_filter: str | None = None
analyses: list[AnalysisEntry] = [] analyses: list[AnalysisEntry] = []

View file

@ -155,6 +155,53 @@ class TestAnalyzeTool:
assert state.analyses[0].answer == "42" assert state.analyses[0].answer == "42"
assert state.analyses[0].program == "print(42)" assert state.analyses[0].program == "print(42)"
async def test_analyze_applies_document_filter_from_state(
self, rag_db, monkeypatch
):
from haiku.rag.skills.rlm import RLMState, create_skill
captured_kwargs = {}
async def mock_rlm(self, question, **kwargs):
captured_kwargs.update(kwargs)
return RLMResult(answer="42", program="print(42)")
monkeypatch.setattr(HaikuRAG, "rlm", mock_rlm)
skill = create_skill(db_path=rag_db)
analyze = _get_tool(skill, "analyze")
state = RLMState(document_filter="title = 'AI Overview'")
ctx = _make_ctx(state)
await analyze(ctx, question="How many documents?")
assert captured_kwargs.get("filter") == "title = 'AI Overview'"
async def test_analyze_combines_state_filter_with_explicit_filter(
self, rag_db, monkeypatch
):
from haiku.rag.skills.rlm import RLMState, create_skill
captured_kwargs = {}
async def mock_rlm(self, question, **kwargs):
captured_kwargs.update(kwargs)
return RLMResult(answer="Result", program="code()")
monkeypatch.setattr(HaikuRAG, "rlm", mock_rlm)
skill = create_skill(db_path=rag_db)
analyze = _get_tool(skill, "analyze")
state = RLMState(document_filter="title = 'AI Overview'")
ctx = _make_ctx(state)
await analyze(
ctx,
question="Count pages",
filter="uri LIKE '%test%'",
)
result_filter = captured_kwargs["filter"]
assert isinstance(result_filter, str)
assert "title = 'AI Overview'" in result_filter
assert "uri LIKE '%test%'" in result_filter
async def test_analyze_with_document_and_filter(self, rag_db, monkeypatch): async def test_analyze_with_document_and_filter(self, rag_db, monkeypatch):
from haiku.rag.skills.rlm import create_skill from haiku.rag.skills.rlm import create_skill