semantic search finalization
This commit is contained in:
@@ -1,86 +1,84 @@
|
||||
// src/pages/api/search/semantic.ts
|
||||
import type { APIRoute } from 'astro';
|
||||
import { getToolsData } from '../../../utils/dataService.js';
|
||||
import { configDotenv } from 'dotenv';
|
||||
|
||||
configDotenv();
|
||||
|
||||
const DEFAULT_MAX_RESULTS = (() => {
|
||||
const raw = process.env.AI_EMBEDDING_CANDIDATES;
|
||||
const n = Number.parseInt(raw ?? '', 10);
|
||||
return Number.isFinite(n) && n > 0 ? n : 50; // fallback
|
||||
})();
|
||||
|
||||
const DEFAULT_THRESHOLD = (() => {
|
||||
const raw = process.env.AI_SIMILARITY_THRESHOLD;
|
||||
const n = Number.parseFloat(raw ?? '');
|
||||
return Number.isFinite(n) && n >= 0 && n <= 1 ? n : 0.45;
|
||||
})();
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
const { query, maxResults = 50, threshold = 0.45 } = await request.json();
|
||||
|
||||
/* ---------- get body & apply defaults from env ---------------- */
|
||||
const {
|
||||
query,
|
||||
maxResults = DEFAULT_MAX_RESULTS,
|
||||
threshold = DEFAULT_THRESHOLD
|
||||
} = await request.json();
|
||||
|
||||
if (!query || typeof query !== 'string') {
|
||||
return new Response(JSON.stringify({
|
||||
success: false,
|
||||
error: 'Query is required'
|
||||
}), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
return new Response(
|
||||
JSON.stringify({ success: false, error: 'Query is required' }),
|
||||
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
// Import embeddings service dynamically
|
||||
/* --- (rest of the handler unchanged) -------------------------- */
|
||||
const { embeddingsService } = await import('../../../utils/embeddings.js');
|
||||
|
||||
// Check if embeddings are available
|
||||
if (!embeddingsService.isEnabled()) {
|
||||
return new Response(JSON.stringify({
|
||||
success: false,
|
||||
error: 'Semantic search not available'
|
||||
}), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
return new Response(
|
||||
JSON.stringify({ success: false, error: 'Semantic search not available' }),
|
||||
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
// Wait for embeddings initialization if needed
|
||||
await embeddingsService.waitForInitialization();
|
||||
|
||||
// Get similar items using embeddings
|
||||
const similarItems = await embeddingsService.findSimilar(
|
||||
query.trim(),
|
||||
maxResults,
|
||||
query.trim(),
|
||||
maxResults,
|
||||
threshold
|
||||
);
|
||||
|
||||
// Get current tools data
|
||||
const toolsData = await getToolsData();
|
||||
|
||||
// Map similarity results back to full tool objects, preserving similarity ranking
|
||||
|
||||
const toolsData = await getToolsData();
|
||||
const rankedTools = similarItems
|
||||
.map((similarItem, index) => {
|
||||
const tool = toolsData.tools.find(t => t.name === similarItem.name);
|
||||
return tool
|
||||
? {
|
||||
...tool,
|
||||
_semanticSimilarity: similarItem.similarity,
|
||||
_semanticRank: index + 1, // already sorted
|
||||
}
|
||||
: null;
|
||||
.map((s, i) => {
|
||||
const tool = toolsData.tools.find(t => t.name === s.name);
|
||||
return tool ? { ...tool, _semanticSimilarity: s.similarity, _semanticRank: i + 1 } : null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
success: true,
|
||||
query: query.trim(),
|
||||
results: rankedTools,
|
||||
totalFound: rankedTools.length,
|
||||
semanticSearch: true,
|
||||
threshold,
|
||||
maxSimilarity: rankedTools.length > 0 ? rankedTools[0]._semanticSimilarity : 0
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
query: query.trim(),
|
||||
results: rankedTools,
|
||||
totalFound: rankedTools.length,
|
||||
semanticSearch: true,
|
||||
threshold,
|
||||
maxSimilarity: rankedTools[0]?._semanticSimilarity ?? 0
|
||||
}),
|
||||
{ status: 200, headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Semantic search error:', error);
|
||||
return new Response(JSON.stringify({
|
||||
success: false,
|
||||
error: 'Semantic search failed'
|
||||
}), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
return new Response(
|
||||
JSON.stringify({ success: false, error: 'Semantic search failed' }),
|
||||
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user