feat(#70): implement semantic search API with Ollama embeddings

Updated semantic search to use OllamaEmbeddingService instead of OpenAI:
- Replaced EmbeddingService with OllamaEmbeddingService in SearchService
- Added configurable similarity threshold (SEMANTIC_SEARCH_SIMILARITY_THRESHOLD)
- Updated both semanticSearch() and hybridSearch() methods
- Added comprehensive tests for semantic search functionality
- Updated controller documentation to reflect Ollama requirement
- All tests passing with 85%+ coverage

Related changes:
- Updated knowledge.service.versions.spec.ts to include OllamaEmbeddingService
- Added similarity threshold environment variable to .env.example

Fixes #70

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-02 15:15:04 -06:00
parent 3dfa603a03
commit 3969dd5598
6 changed files with 332 additions and 21 deletions

View File

@@ -5,6 +5,8 @@ import { PrismaService } from "../prisma/prisma.service";
import { LinkSyncService } from "./services/link-sync.service";
import { KnowledgeCacheService } from "./services/cache.service";
import { EmbeddingService } from "./services/embedding.service";
import { OllamaEmbeddingService } from "./services/ollama-embedding.service";
import { EmbeddingQueueService } from "./queues/embedding-queue.service";
import { NotFoundException } from "@nestjs/common";
describe("KnowledgeService - Version History", () => {
@@ -125,6 +127,17 @@ describe("KnowledgeService - Version History", () => {
batchGenerateEmbeddings: vi.fn().mockResolvedValue([]),
};
const mockOllamaEmbeddingService = {
isConfigured: vi.fn().mockResolvedValue(false),
generateEmbedding: vi.fn().mockResolvedValue([]),
generateAndStoreEmbedding: vi.fn().mockResolvedValue(undefined),
batchGenerateEmbeddings: vi.fn().mockResolvedValue(0),
};
const mockEmbeddingQueueService = {
enqueueEmbeddingGeneration: vi.fn().mockResolvedValue(undefined),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
@@ -145,6 +158,14 @@ describe("KnowledgeService - Version History", () => {
provide: EmbeddingService,
useValue: mockEmbeddingService,
},
{
provide: OllamaEmbeddingService,
useValue: mockOllamaEmbeddingService,
},
{
provide: EmbeddingQueueService,
useValue: mockEmbeddingQueueService,
},
],
}).compile();
@@ -329,7 +350,13 @@ describe("KnowledgeService - Version History", () => {
// Mock for findVersion call
mockPrismaService.knowledgeEntry.findUnique.mockResolvedValueOnce(entryWithVersions);
const result = await service.restoreVersion(workspaceId, slug, 2, userId, "Custom restore note");
const result = await service.restoreVersion(
workspaceId,
slug,
2,
userId,
"Custom restore note"
);
expect(result.title).toBe("Test Entry v2");
expect(result.content).toBe("# Version 2");