Fix API endpoint paths and search threshold

- Changed API_BASE_URL default to http://192.168.1.16:8001
- Added /api prefix normalization to apiRequest
- Fixed search to use POST with low threshold (0.01) for embedding scores
- Fixed get_stats to use total_documents/total_chunks field names
- Fixed list_documents to use stats+tags endpoints (no /documents endpoint)
- Added embedding_model and chat_model to stats output
main
Clawd 3 weeks ago
parent 1f9cdcd922
commit 1ae3e2a0b6

@ -19,10 +19,15 @@ import * as fs from "fs";
import * as path from "path"; import * as path from "path";
// Configuration - can be overridden via environment variables // 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_USERNAME = process.env.SECOND_BRAIN_USERNAME || "";
const API_PASSWORD = process.env.SECOND_BRAIN_PASSWORD || ""; 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 // Build auth header if credentials provided
function getAuthHeader(): Record<string, string> { function getAuthHeader(): Record<string, string> {
if (API_USERNAME && API_PASSWORD) { if (API_USERNAME && API_PASSWORD) {
@ -37,7 +42,11 @@ async function apiRequest(
endpoint: string, endpoint: string,
options: RequestInit = {} options: RequestInit = {}
): Promise<Response> { ): Promise<Response> {
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 = { const headers = {
...getAuthHeader(), ...getAuthHeader(),
...options.headers, ...options.headers,
@ -321,12 +330,17 @@ async function handleSearch(args: {
query: string; query: string;
top_k?: number; top_k?: number;
}): Promise<string> { }): Promise<string> {
const params = new URLSearchParams({ // Use POST with JSON body for more control over search parameters
q: args.query, const response = await apiRequest(`/v1/search`, {
top_k: String(args.top_k || 5), 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(); const data = await response.json();
if (!response.ok) { if (!response.ok) {
@ -450,31 +464,35 @@ async function handleListDocuments(args: {
folder?: string; folder?: string;
limit?: number; limit?: number;
}): Promise<string> { }): Promise<string> {
const params = new URLSearchParams({ // The /documents endpoint doesn't exist, so we use search with a broad query
limit: String(args.limit || 20), // or get stats to show document count
}); const statsResponse = await apiRequest("/v1/stats");
if (args.folder) params.append("folder", args.folder); const stats = await statsResponse.json();
const response = await apiRequest(`/v1/documents?${params}`);
const data = await response.json();
if (!response.ok) { if (!statsResponse.ok) {
throw new Error( 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) { // Also get tags to show what's in the vault
return "No documents found."; 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`; if (Array.isArray(tags) && tags.length > 0) {
for (const doc of data.documents) { output += `**Tags:**\n`;
output += `- **${doc.title || doc.path}**`; for (const tag of tags) {
if (doc.tags?.length) output += ` [${doc.tags.join(", ")}]`; output += `- #${tag.tag} (${tag.count} docs)\n`;
output += "\n"; }
} }
output += `\n_Use search to find specific documents._`;
return output; return output;
} }
@ -489,10 +507,12 @@ async function handleGetStats(): Promise<string> {
} }
return `📊 Second Brain Stats: return `📊 Second Brain Stats:
- Documents: ${data.document_count || 0} - Documents: ${data.total_documents || data.document_count || 0}
- Chunks: ${data.chunk_count || 0} - Chunks: ${data.total_chunks || data.chunk_count || 0}
- Vault size: ${data.vault_size_mb?.toFixed(2) || 0} MB - Vault size: ${data.vault_size_mb?.toFixed(2) || "N/A"} MB
- Last indexed: ${data.last_indexed || "Never"}`; - Last indexed: ${data.last_indexed || "Never"}
- Embedding model: ${data.embedding_model || "N/A"}
- Chat model: ${data.chat_model || "N/A"}`;
} }
async function handleReindex(): Promise<string> { async function handleReindex(): Promise<string> {

Loading…
Cancel
Save

Powered by TurnKey Linux.