memory / services /router.js
gcharanteja
feat: enhance search handling to provide user-friendly messages and formatted results
0c3ae10
Raw
History Blame Contribute Delete
4.22 kB
import { askOpenAI } from "./llm.js";
import { curateText } from "./curation.js";
import { queryCollectionDataEnhanced, queryCollectionData } from "./vector.js";
import { loadContextTreeDocuments } from "../utils.js";
export async function routeUserInput(input, config) {
try {
const intent = await classifyIntent(input, config);
switch (intent.type) {
case "search":
return await handleSearch(input, intent, config);
case "curate":
return await handleCurate(input, intent, config);
case "add":
return await handleAdd(input, intent, config);
case "list":
return await handleList(input, intent, config);
default:
return {
error: "Could not determine intent",
input,
intent: intent.type,
};
}
} catch (error) {
return {
error: `Routing failed: ${error.message}`,
input,
};
}
}
async function classifyIntent(input, config) {
const prompt = `Analyze this user input and classify the intent. Return ONLY valid JSON:
Input: "${input}"
Classify as one of these intents:
- "search": User is asking questions to find knowledge
- "curate": User is teaching/storing new knowledge
- "add": User is adding documents to the knowledge base
- "list": User wants to see all stored knowledge
Response format (valid JSON only):
{
"type": "search|curate|add|list",
"confidence": 0.0-1.0,
"reasoning": "brief explanation",
"hints": {
"enhanced": false,
"rerank": false,
"extractText": null
}
}`;
try {
const response = await askOpenAI(input, prompt);
const parsed = JSON.parse(response);
return parsed;
} catch (error) {
console.error("Intent classification failed:", error.message);
return { type: "search", confidence: 0.5, reasoning: "Fallback to search" };
}
}
async function handleSearch(input, intent, config) {
try {
const searchParams = {
query: input,
nResults: 5,
enhanced: intent.hints?.enhanced || false,
rerank: intent.hints?.rerank || false,
};
const results = searchParams.enhanced
? await queryCollectionDataEnhanced(searchParams)
: await queryCollectionData(searchParams);
const documents = results.ids?.[0] || [];
const contents = results.documents?.[0] || [];
const metadatas = results.metadatas?.[0] || [];
if (documents.length === 0) {
return {
intent: "search",
query: input,
answer: "No matching knowledge found. Please curate relevant documentation first.",
};
}
const answer = contents
.map((content, idx) => {
const meta = metadatas[idx] || {};
return `**${meta.title || documents[idx]}** (${meta.topic || "general"})\n${content}`;
})
.join("\n\n---\n\n");
return {
intent: "search",
query: input,
answer,
};
} catch (error) {
return {
intent: "search",
error: `Search failed: ${error.message}`,
query: input,
};
}
}
async function handleCurate(input, intent, config) {
try {
const textToCurate = intent.hints?.extractText || input;
const result = await curateText(textToCurate, config);
return {
intent: "curate",
action: "extracted_and_stored",
result,
};
} catch (error) {
return {
intent: "curate",
error: `Curation failed: ${error.message}`,
input,
};
}
}
async function handleAdd(input, intent, config) {
return {
intent: "add",
message:
"To add documents, use the add_documents tool with an array of {id, text, metadata}",
guidance: input,
};
}
async function handleList(input, intent, config) {
try {
const dataDir = process.env.DATA_DIR || "/data";
const docs = await loadContextTreeDocuments(dataDir);
return {
intent: "list",
count: docs.length,
documents: docs.map((d) => ({
id: d.id,
title: d.title,
topic: d.topic,
type: d.type,
importance: d.importance,
filePath: d.filePath,
})),
};
} catch (error) {
return {
intent: "list",
error: `List failed: ${error.message}`,
};
}
}