semantic search finalization

This commit is contained in:
overcuriousity
2025-08-06 23:10:57 +02:00
parent 0cab3b6945
commit 3462f049e4
10 changed files with 1466 additions and 395 deletions

View File

@@ -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' } }
);
}
};