Add tests for None event_date in fact extraction (AttributeError fix) and for _register_profile skipping .env overwrite with short config keys.
This commit is contained in:
parent
cf4bd598b4
commit
66cbdda3cb
2 changed files with 116 additions and 0 deletions
|
|
@ -139,3 +139,76 @@ async def test_retain_llm_max_retries_overrides_global():
|
||||||
assert facts == []
|
assert facts == []
|
||||||
# Verify it retried exactly retain_llm_max_retries times
|
# Verify it retried exactly retain_llm_max_retries times
|
||||||
assert llm_config.call.call_count == 5
|
assert llm_config.call.call_count == 5
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_none_event_date_with_empty_facts_no_crash():
|
||||||
|
"""
|
||||||
|
When event_date is None and the LLM returns an empty facts list,
|
||||||
|
the debug log should not crash with AttributeError on .isoformat().
|
||||||
|
|
||||||
|
Regression test for https://github.com/vectorize-io/hindsight/issues/874
|
||||||
|
"""
|
||||||
|
from hindsight_api.engine.retain.fact_extraction import _extract_facts_from_chunk
|
||||||
|
|
||||||
|
config = _make_config(llm_max_retries=1)
|
||||||
|
|
||||||
|
# LLM returns a valid dict but with no facts — triggers the debug log path
|
||||||
|
llm_config = _make_llm_config(mock_response={"facts": []})
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"hindsight_api.engine.retain.fact_extraction._build_extraction_prompt_and_schema",
|
||||||
|
return_value=("system prompt", MagicMock()),
|
||||||
|
):
|
||||||
|
facts, usage = await _extract_facts_from_chunk(
|
||||||
|
chunk="A plain text document with no timestamp.",
|
||||||
|
chunk_index=0,
|
||||||
|
total_chunks=1,
|
||||||
|
event_date=None,
|
||||||
|
context="",
|
||||||
|
llm_config=llm_config,
|
||||||
|
config=config,
|
||||||
|
agent_name="test-agent",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert facts == []
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_none_event_date_with_valid_facts_no_crash():
|
||||||
|
"""
|
||||||
|
When event_date is None but the LLM returns valid facts,
|
||||||
|
extraction should succeed without errors.
|
||||||
|
"""
|
||||||
|
from hindsight_api.engine.retain.fact_extraction import _extract_facts_from_chunk
|
||||||
|
|
||||||
|
config = _make_config(llm_max_retries=1)
|
||||||
|
|
||||||
|
llm_config = _make_llm_config(mock_response={
|
||||||
|
"facts": [
|
||||||
|
{
|
||||||
|
"what": "Alice visited Paris",
|
||||||
|
"when": "2023",
|
||||||
|
"who": "Alice",
|
||||||
|
"why": "vacation",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"hindsight_api.engine.retain.fact_extraction._build_extraction_prompt_and_schema",
|
||||||
|
return_value=("system prompt", MagicMock()),
|
||||||
|
):
|
||||||
|
facts, usage = await _extract_facts_from_chunk(
|
||||||
|
chunk="Alice visited Paris in 2023.",
|
||||||
|
chunk_index=0,
|
||||||
|
total_chunks=1,
|
||||||
|
event_date=None,
|
||||||
|
context="",
|
||||||
|
llm_config=llm_config,
|
||||||
|
config=config,
|
||||||
|
agent_name="test-agent",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(facts) == 1
|
||||||
|
assert "Alice visited Paris" in facts[0].fact
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
"""Tests for EmbedManager interface."""
|
"""Tests for EmbedManager interface."""
|
||||||
|
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from hindsight_embed import get_embed_manager
|
from hindsight_embed import get_embed_manager
|
||||||
|
from hindsight_embed.daemon_embed_manager import DaemonEmbedManager
|
||||||
|
|
||||||
|
|
||||||
def test_sanitize_profile_name_via_db_url():
|
def test_sanitize_profile_name_via_db_url():
|
||||||
|
|
@ -51,3 +54,43 @@ def test_manager_singleton():
|
||||||
|
|
||||||
# They should produce the same results
|
# They should produce the same results
|
||||||
assert manager1.get_database_url("test") == manager2.get_database_url("test")
|
assert manager1.get_database_url("test") == manager2.get_database_url("test")
|
||||||
|
|
||||||
|
|
||||||
|
def test_register_profile_skips_when_no_api_keys():
|
||||||
|
"""
|
||||||
|
When config contains only short keys (no HINDSIGHT_API_* prefix),
|
||||||
|
_register_profile should not call create_profile, preserving any
|
||||||
|
existing profile .env file.
|
||||||
|
|
||||||
|
Regression test for https://github.com/vectorize-io/hindsight/issues/894
|
||||||
|
"""
|
||||||
|
manager = DaemonEmbedManager()
|
||||||
|
manager._profile_manager = MagicMock()
|
||||||
|
|
||||||
|
# Config with short keys (as passed from cli.py's get_config())
|
||||||
|
config = {"llm_api_key": "sk-123", "llm_provider": "openai", "llm_model": "gpt-4o"}
|
||||||
|
manager._register_profile("myprofile", 8100, config)
|
||||||
|
|
||||||
|
manager._profile_manager.create_profile.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_register_profile_calls_create_when_api_keys_present():
|
||||||
|
"""
|
||||||
|
When config contains HINDSIGHT_API_* keys, _register_profile should
|
||||||
|
forward them to create_profile.
|
||||||
|
"""
|
||||||
|
manager = DaemonEmbedManager()
|
||||||
|
manager._profile_manager = MagicMock()
|
||||||
|
|
||||||
|
config = {
|
||||||
|
"HINDSIGHT_API_LLM_PROVIDER": "openai",
|
||||||
|
"HINDSIGHT_API_LLM_API_KEY": "sk-123",
|
||||||
|
"some_internal_key": "ignored",
|
||||||
|
}
|
||||||
|
manager._register_profile("myprofile", 8100, config)
|
||||||
|
|
||||||
|
manager._profile_manager.create_profile.assert_called_once_with(
|
||||||
|
"myprofile",
|
||||||
|
8100,
|
||||||
|
{"HINDSIGHT_API_LLM_PROVIDER": "openai", "HINDSIGHT_API_LLM_API_KEY": "sk-123"},
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue