feat(rag): close eval pipeline with live snapshots
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.superbiz.agent.service;
|
||||
|
||||
import com.superbiz.agent.dto.Frontmatter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
@@ -38,4 +39,16 @@ class DocumentManagementServiceTest {
|
||||
assertEquals("payment/runbook.md", storedPath);
|
||||
assertTrue(Files.exists(tempDir.resolve("payment").resolve("runbook.md")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDocumentIdUsesFrontmatterSourceWhenItFitsDatabaseColumn() {
|
||||
DocumentManagementService service = new DocumentManagementService();
|
||||
Frontmatter frontmatter = Frontmatter.builder()
|
||||
.source("mysql-connection-pool")
|
||||
.build();
|
||||
|
||||
String docId = ReflectionTestUtils.invokeMethod(service, "resolveDocumentId", frontmatter);
|
||||
|
||||
assertEquals("mysql-connection-pool", docId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,4 +146,48 @@ class FrontmatterParserTest {
|
||||
assertEquals("1.0.0", result.getVersion());
|
||||
assertEquals("Test Author", result.getAuthor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testParse_withRetrievalMetadata() {
|
||||
String content = """
|
||||
---
|
||||
title: MySQL Connection Pool
|
||||
keywords: [connection pool, HikariCP]
|
||||
summary: Diagnose exhausted MySQL connection pools
|
||||
category: database
|
||||
source: mysql-connection-pool
|
||||
breadcrumb: Database > MySQL > Connection Pool
|
||||
kb_scope: rag-eval
|
||||
---
|
||||
Content
|
||||
""";
|
||||
|
||||
Frontmatter result = parser.parse(content);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals("mysql-connection-pool", result.getSource());
|
||||
assertEquals("Database > MySQL > Connection Pool", result.getBreadcrumb());
|
||||
assertEquals("rag-eval", result.getKbScope());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStripFrontmatter_returnsMarkdownBodyOnly() {
|
||||
String content = """
|
||||
---
|
||||
title: Test
|
||||
keywords: [frontmatter-only]
|
||||
summary: Summary
|
||||
---
|
||||
|
||||
# Body
|
||||
|
||||
Body content
|
||||
""";
|
||||
|
||||
String body = parser.stripFrontmatter(content);
|
||||
|
||||
assertFalse(body.contains("frontmatter-only"));
|
||||
assertTrue(body.startsWith("# Body"));
|
||||
assertTrue(body.contains("Body content"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,49 @@ class KnowledgeIndexServiceTest {
|
||||
assertNull(hint.singleDomainOrNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnalyzeQuery_filtersByConfiguredKbScope() {
|
||||
ReflectionTestUtils.setField(service, "kbScope", "rag-eval");
|
||||
service.addToIndex(KnowledgeEntry.builder()
|
||||
.filePath("legacy.md")
|
||||
.keywords(List.of("timeout"))
|
||||
.category("legacy")
|
||||
.build());
|
||||
service.addToIndex(KnowledgeEntry.builder()
|
||||
.filePath("eval.md")
|
||||
.keywords(List.of("timeout"))
|
||||
.category("eval")
|
||||
.kbScope("rag-eval")
|
||||
.build());
|
||||
|
||||
KnowledgeIndexService.L0Hint hint = service.analyzeQuery("timeout");
|
||||
|
||||
assertEquals(1, hint.matches().size());
|
||||
assertEquals(List.of("eval"), hint.domains());
|
||||
assertEquals("eval", hint.singleDomainOrNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnalyzeQuery_keepsLegacyEntriesWhenNoScopeConfigured() {
|
||||
ReflectionTestUtils.setField(service, "kbScope", "");
|
||||
service.addToIndex(KnowledgeEntry.builder()
|
||||
.filePath("legacy.md")
|
||||
.keywords(List.of("timeout"))
|
||||
.category("legacy")
|
||||
.build());
|
||||
service.addToIndex(KnowledgeEntry.builder()
|
||||
.filePath("eval.md")
|
||||
.keywords(List.of("timeout"))
|
||||
.category("eval")
|
||||
.kbScope("rag-eval")
|
||||
.build());
|
||||
|
||||
KnowledgeIndexService.L0Hint hint = service.analyzeQuery("timeout");
|
||||
|
||||
assertEquals(2, hint.matches().size());
|
||||
assertNull(hint.singleDomainOrNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExactMatch_noMatch() {
|
||||
KnowledgeEntry entry = KnowledgeEntry.builder()
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.superbiz.agent.service;
|
||||
|
||||
import com.superbiz.agent.dto.DocumentChunk;
|
||||
import com.superbiz.agent.dto.Frontmatter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class VectorIndexServiceTest {
|
||||
@@ -34,4 +37,35 @@ class VectorIndexServiceTest {
|
||||
|
||||
assertEquals("Plain chunk content.", VectorIndexService.buildEmbeddingText(chunk));
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDocumentMetadataUsesFrontmatterRetrievalFields() {
|
||||
DocumentChunk chunk = DocumentChunk.builder()
|
||||
.chunkIndex(0)
|
||||
.title("Chunk Title")
|
||||
.breadcrumb("Chunk > Path")
|
||||
.content("content")
|
||||
.build();
|
||||
Frontmatter frontmatter = Frontmatter.builder()
|
||||
.title("Document Title")
|
||||
.source("mysql-connection-pool")
|
||||
.breadcrumb("Database > MySQL > Connection Pool")
|
||||
.kbScope("rag-eval")
|
||||
.build();
|
||||
|
||||
Map<String, Object> metadata = VectorIndexService.buildDocumentMetadata(
|
||||
"mysql-connection-pool",
|
||||
chunk,
|
||||
2,
|
||||
"database",
|
||||
frontmatter
|
||||
);
|
||||
|
||||
assertEquals("mysql-connection-pool", metadata.get("docId"));
|
||||
assertEquals("mysql-connection-pool", metadata.get("_source"));
|
||||
assertEquals("mysql-connection-pool", metadata.get("source"));
|
||||
assertEquals("database", metadata.get("category"));
|
||||
assertEquals("rag-eval", metadata.get("kb_scope"));
|
||||
assertEquals("Database > MySQL > Connection Pool", metadata.get("breadcrumb"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
@@ -151,6 +152,45 @@ class VectorSearchServiceTest {
|
||||
assertTrue(request.toString().contains("api"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void springModeUsesVectorStoreAlias() {
|
||||
VectorStore vectorStore = mock(VectorStore.class);
|
||||
ObjectProvider<VectorStore> provider = mock(ObjectProvider.class);
|
||||
when(provider.getIfAvailable()).thenReturn(vectorStore);
|
||||
when(vectorStore.similaritySearch(any(SearchRequest.class))).thenReturn(List.of());
|
||||
|
||||
VectorSearchService service = new VectorSearchService();
|
||||
setMode(service, "spring");
|
||||
setVectorStore(service, provider);
|
||||
|
||||
service.searchSimilarDocuments("query", 5, "api");
|
||||
|
||||
verify(vectorStore).similaritySearch(any(SearchRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultScopeDoesNotAddMetadataFilter() {
|
||||
VectorSearchService service = new VectorSearchService();
|
||||
setKbScope(service, "");
|
||||
|
||||
assertNull(service.buildSpringAiFilterExpression(null));
|
||||
assertNull(service.buildSdkFilterExpression(null));
|
||||
assertEquals("category == 'api'", service.buildSpringAiFilterExpression("api"));
|
||||
assertEquals("metadata[\"category\"] == \"api\"", service.buildSdkFilterExpression("api"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void configuredScopeCombinesWithCategoryFilter() {
|
||||
VectorSearchService service = new VectorSearchService();
|
||||
setKbScope(service, "rag-eval");
|
||||
|
||||
assertEquals("kb_scope == 'rag-eval'", service.buildSpringAiFilterExpression(null));
|
||||
assertEquals("category == 'api' && kb_scope == 'rag-eval'",
|
||||
service.buildSpringAiFilterExpression("api"));
|
||||
assertEquals("metadata[\"category\"] == \"api\" && metadata[\"kb_scope\"] == \"rag-eval\"",
|
||||
service.buildSdkFilterExpression("api"));
|
||||
}
|
||||
|
||||
private static void setMode(VectorSearchService service, String mode) {
|
||||
ReflectionTestUtils.setField(service, "vectorStoreMode", mode);
|
||||
}
|
||||
@@ -161,6 +201,10 @@ class VectorSearchServiceTest {
|
||||
ReflectionTestUtils.setField(service, "maxL2Distance", 2.0);
|
||||
}
|
||||
|
||||
private static void setKbScope(VectorSearchService service, String kbScope) {
|
||||
ReflectionTestUtils.setField(service, "kbScope", kbScope);
|
||||
}
|
||||
|
||||
private static VectorSearchService.SearchResult result(String id, float score) {
|
||||
VectorSearchService.SearchResult result = new VectorSearchService.SearchResult();
|
||||
result.setId(id);
|
||||
|
||||
Reference in New Issue
Block a user