76 lines
1.7 KiB
JavaScript
76 lines
1.7 KiB
JavaScript
const DEFAULT_EXA_API_URL = 'https://api.exa.ai/search';
|
|
|
|
const buildSummary = (results) => {
|
|
if (!results || results.length === 0) {
|
|
return '';
|
|
}
|
|
|
|
const snippets = results
|
|
.map((result) => result.snippet || result.highlight)
|
|
.filter(Boolean);
|
|
|
|
if (snippets.length === 0) {
|
|
return results
|
|
.slice(0, 3)
|
|
.map((result) => result.title)
|
|
.filter(Boolean)
|
|
.join(' · ');
|
|
}
|
|
|
|
return snippets.slice(0, 3).join(' ');
|
|
};
|
|
|
|
const searchExerciseResearch = async ({ query, numResults = 5 }) => {
|
|
if (!query || typeof query !== 'string') {
|
|
throw new Error('Query must be a non-empty string');
|
|
}
|
|
|
|
const apiKey = process.env.EXA_API_KEY;
|
|
if (!apiKey) {
|
|
throw new Error('EXA_API_KEY is not configured');
|
|
}
|
|
|
|
const apiUrl = process.env.EXA_API_URL || DEFAULT_EXA_API_URL;
|
|
|
|
const response = await fetch(apiUrl, {
|
|
method: 'POST',
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
'x-api-key': apiKey
|
|
},
|
|
body: JSON.stringify({
|
|
query,
|
|
numResults,
|
|
type: 'neural',
|
|
useAutoprompt: true
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const text = await response.text();
|
|
throw new Error(`Exa search failed: ${response.status} ${text}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
const results = (data.results || []).map((result) => ({
|
|
id: result.id,
|
|
title: result.title,
|
|
url: result.url,
|
|
snippet: Array.isArray(result.highlights) && result.highlights.length > 0
|
|
? result.highlights[0]
|
|
: result.snippet,
|
|
highlight: result.highlight,
|
|
publishedDate: result.publishedDate,
|
|
score: result.score
|
|
}));
|
|
|
|
return {
|
|
summary: buildSummary(results),
|
|
results
|
|
};
|
|
};
|
|
|
|
module.exports = {
|
|
searchExerciseResearch
|
|
};
|