feat(timeline): upgrade seed phase to hybrid search
Replace FTS-only seed entity discovery with hybrid search (FTS + vector via RRF), using the same search_hybrid infrastructure as the search command. Falls back gracefully to FTS-only when Ollama is unavailable. Changes: - seed_timeline() now accepts OllamaClient, delegates to search_hybrid - New resolve_documents_to_entities() replaces find_seed_entities() - SeedResult gains search_mode field tracking actual mode used - TimelineResult carries search_mode through to JSON renderer - run_timeline wires up OllamaClient from config - handle_timeline made async for the hybrid search await - Tests updated for new function signatures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -108,8 +108,8 @@ fn insert_label_event(
|
||||
|
||||
/// Full pipeline: seed -> expand -> collect for a scenario with an issue
|
||||
/// that has a closing MR, state changes, and label events.
|
||||
#[test]
|
||||
fn pipeline_seed_expand_collect_end_to_end() {
|
||||
#[tokio::test]
|
||||
async fn pipeline_seed_expand_collect_end_to_end() {
|
||||
let conn = setup_db();
|
||||
let project_id = insert_project(&conn, "group/project");
|
||||
|
||||
@@ -149,7 +149,9 @@ fn pipeline_seed_expand_collect_end_to_end() {
|
||||
insert_label_event(&conn, project_id, Some(issue_id), "bug", 1500);
|
||||
|
||||
// SEED: find entities matching "authentication"
|
||||
let seed_result = seed_timeline(&conn, "authentication", None, None, 50, 10).unwrap();
|
||||
let seed_result = seed_timeline(&conn, None, "authentication", None, None, 50, 10)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(
|
||||
!seed_result.seed_entities.is_empty(),
|
||||
"Seed should find at least one entity"
|
||||
@@ -213,12 +215,14 @@ fn pipeline_seed_expand_collect_end_to_end() {
|
||||
}
|
||||
|
||||
/// Verify the pipeline handles an empty FTS result gracefully.
|
||||
#[test]
|
||||
fn pipeline_empty_query_produces_empty_result() {
|
||||
#[tokio::test]
|
||||
async fn pipeline_empty_query_produces_empty_result() {
|
||||
let conn = setup_db();
|
||||
let _project_id = insert_project(&conn, "group/project");
|
||||
|
||||
let seed_result = seed_timeline(&conn, "", None, None, 50, 10).unwrap();
|
||||
let seed_result = seed_timeline(&conn, None, "", None, None, 50, 10)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(seed_result.seed_entities.is_empty());
|
||||
|
||||
let expand_result = expand_timeline(&conn, &seed_result.seed_entities, 1, false, 100).unwrap();
|
||||
@@ -237,8 +241,8 @@ fn pipeline_empty_query_produces_empty_result() {
|
||||
}
|
||||
|
||||
/// Verify since filter propagates through the full pipeline.
|
||||
#[test]
|
||||
fn pipeline_since_filter_excludes_old_events() {
|
||||
#[tokio::test]
|
||||
async fn pipeline_since_filter_excludes_old_events() {
|
||||
let conn = setup_db();
|
||||
let project_id = insert_project(&conn, "group/project");
|
||||
|
||||
@@ -255,7 +259,9 @@ fn pipeline_since_filter_excludes_old_events() {
|
||||
insert_state_event(&conn, project_id, Some(issue_id), None, "closed", 2000);
|
||||
insert_state_event(&conn, project_id, Some(issue_id), None, "reopened", 8000);
|
||||
|
||||
let seed_result = seed_timeline(&conn, "deploy", None, None, 50, 10).unwrap();
|
||||
let seed_result = seed_timeline(&conn, None, "deploy", None, None, 50, 10)
|
||||
.await
|
||||
.unwrap();
|
||||
let expand_result = expand_timeline(&conn, &seed_result.seed_entities, 0, false, 100).unwrap();
|
||||
|
||||
// Collect with since=5000: should exclude Created(1000) and closed(2000)
|
||||
@@ -274,8 +280,8 @@ fn pipeline_since_filter_excludes_old_events() {
|
||||
}
|
||||
|
||||
/// Verify unresolved references use Option<i64> for target_iid.
|
||||
#[test]
|
||||
fn pipeline_unresolved_refs_have_optional_iid() {
|
||||
#[tokio::test]
|
||||
async fn pipeline_unresolved_refs_have_optional_iid() {
|
||||
let conn = setup_db();
|
||||
let project_id = insert_project(&conn, "group/project");
|
||||
|
||||
@@ -302,7 +308,9 @@ fn pipeline_unresolved_refs_have_optional_iid() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let seed_result = seed_timeline(&conn, "cross project", None, None, 50, 10).unwrap();
|
||||
let seed_result = seed_timeline(&conn, None, "cross project", None, None, 50, 10)
|
||||
.await
|
||||
.unwrap();
|
||||
let expand_result = expand_timeline(&conn, &seed_result.seed_entities, 1, false, 100).unwrap();
|
||||
|
||||
assert_eq!(expand_result.unresolved_references.len(), 2);
|
||||
|
||||
Reference in New Issue
Block a user