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 };