diff --git a/src/index.ts b/src/index.ts index 4b912cd..d186e5a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,10 +19,15 @@ import * as fs from "fs"; import * as path from "path"; // Configuration - can be overridden via environment variables -const API_BASE_URL = process.env.SECOND_BRAIN_API_URL || "https://2brain.coer.nl/api"; +// API_BASE_URL should point to the base (e.g., http://192.168.1.16:8001) +// The /api/v1 prefix is added by apiRequest +const API_BASE_URL = (process.env.SECOND_BRAIN_API_URL || "http://192.168.1.16:8001").replace(/\/+$/, ""); const API_USERNAME = process.env.SECOND_BRAIN_USERNAME || ""; const API_PASSWORD = process.env.SECOND_BRAIN_PASSWORD || ""; +// Default search threshold - embedding scores are often low, so use a low default +const DEFAULT_SEARCH_THRESHOLD = 0.01; + // Build auth header if credentials provided function getAuthHeader(): Record { if (API_USERNAME && API_PASSWORD) { @@ -37,7 +42,11 @@ async function apiRequest( endpoint: string, options: RequestInit = {} ): Promise { - const url = `${API_BASE_URL}${endpoint}`; + // Ensure endpoint starts with /api/v1 + const normalizedEndpoint = endpoint.startsWith("/api/v1") + ? endpoint + : `/api${endpoint.startsWith("/v1") ? endpoint : `/v1${endpoint}`}`; + const url = `${API_BASE_URL}${normalizedEndpoint}`; const headers = { ...getAuthHeader(), ...options.headers, @@ -321,12 +330,17 @@ async function handleSearch(args: { query: string; top_k?: number; }): Promise { - const params = new URLSearchParams({ - q: args.query, - top_k: String(args.top_k || 5), + // Use POST with JSON body for more control over search parameters + const response = await apiRequest(`/v1/search`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + query: args.query, + limit: args.top_k || 5, + threshold: DEFAULT_SEARCH_THRESHOLD, + hybrid: true, + }), }); - - const response = await apiRequest(`/v1/search?${params}`); const data = await response.json(); if (!response.ok) { @@ -450,31 +464,35 @@ async function handleListDocuments(args: { folder?: string; limit?: number; }): Promise { - const params = new URLSearchParams({ - limit: String(args.limit || 20), - }); - if (args.folder) params.append("folder", args.folder); - - const response = await apiRequest(`/v1/documents?${params}`); - const data = await response.json(); + // The /documents endpoint doesn't exist, so we use search with a broad query + // or get stats to show document count + const statsResponse = await apiRequest("/v1/stats"); + const stats = await statsResponse.json(); - if (!response.ok) { + if (!statsResponse.ok) { throw new Error( - `Failed to list documents: ${data.detail || response.statusText}` + `Failed to get stats: ${stats.detail || statsResponse.statusText}` ); } - if (!data.documents || data.documents.length === 0) { - return "No documents found."; - } + // Also get tags to show what's in the vault + const tagsResponse = await apiRequest("/v1/tags"); + const tags = await tagsResponse.json(); + + let output = `📚 Knowledge Base Overview:\n\n`; + output += `- **Documents:** ${stats.total_documents || 0}\n`; + output += `- **Chunks:** ${stats.total_chunks || 0}\n`; + output += `- **Last indexed:** ${stats.last_indexed || "Never"}\n\n`; - let output = `📚 Documents (${data.total}):\n\n`; - for (const doc of data.documents) { - output += `- **${doc.title || doc.path}**`; - if (doc.tags?.length) output += ` [${doc.tags.join(", ")}]`; - output += "\n"; + if (Array.isArray(tags) && tags.length > 0) { + output += `**Tags:**\n`; + for (const tag of tags) { + output += `- #${tag.tag} (${tag.count} docs)\n`; + } } + output += `\n_Use search to find specific documents._`; + return output; } @@ -489,10 +507,12 @@ async function handleGetStats(): Promise { } return `📊 Second Brain Stats: -- Documents: ${data.document_count || 0} -- Chunks: ${data.chunk_count || 0} -- Vault size: ${data.vault_size_mb?.toFixed(2) || 0} MB -- Last indexed: ${data.last_indexed || "Never"}`; +- Documents: ${data.total_documents || data.document_count || 0} +- Chunks: ${data.total_chunks || data.chunk_count || 0} +- Vault size: ${data.vault_size_mb?.toFixed(2) || "N/A"} MB +- Last indexed: ${data.last_indexed || "Never"} +- Embedding model: ${data.embedding_model || "N/A"} +- Chat model: ${data.chat_model || "N/A"}`; } async function handleReindex(): Promise {