/** * Note Search Tool * Uses the unified SemanticSearchService (FTS + pgvector + RRF). */ import { tool } from 'ai' import { z } from 'zod' import { toolRegistry } from './registry' import { semanticSearchService } from '@/lib/ai/services/semantic-search.service' toolRegistry.register({ name: 'note_search', description: 'Search the user\'s notes using hybrid semantic + keyword search. Returns matching notes with titles and content excerpts.', isInternal: true, buildTool: (ctx) => tool({ description: 'Search the user\'s notes by keyword or semantic meaning. Returns matching notes with titles and content excerpts. Optionally restrict to a specific notebook.', inputSchema: z.object({ query: z.string().describe('The search query'), limit: z.number().optional().describe('Max results to return (default 5)').default(5), notebookId: z.string().optional().describe('Optional notebook ID to restrict search to a specific notebook'), }), execute: async ({ query, limit = 5, notebookId: explicitNotebookId }) => { const notebookId = explicitNotebookId || ctx.notebookId try { const results = await semanticSearchService.searchAsUser(ctx.userId, query, { limit, threshold: 0.25, notebookId }) return results.map(r => ({ id: r.noteId, title: r.title || 'Untitled', excerpt: r.content.substring(0, 300), score: r.score, matchType: r.matchType, })) } catch (e: any) { return { error: `Note search failed: ${e.message}` } } }, }), })