Add Brave Search as primary web search with NanoGPT fallback
Deploy to NAS / deploy (push) Has been cancelled
Deploy to NAS / deploy (push) Has been cancelled
- Brave Search API is now primary (if BRAVE_API_KEY set) - NanoGPT web search is fallback if Brave fails - Add BRAVE_API_KEY to .env, docker-compose, and deploy workflow - Tool calling still works for AI to request searches
This commit is contained in:
+84
-16
@@ -7,6 +7,45 @@ const DEFAULT_MODEL = 'GLM-4.5-Air-Derestricted';
|
||||
const FALLBACK_MODEL = 'GLM-4.7-Flash';
|
||||
const FALLBACK_API_URL = 'https://api.z.ai/api/coding/paas/v4/chat/completions';
|
||||
|
||||
// Brave Search API
|
||||
const BRAVE_API_URL = 'https://api.search.brave.com/res/v1/web/search';
|
||||
|
||||
async function performBraveSearch(query, count = 10) {
|
||||
const response = await axios.get(BRAVE_API_URL, {
|
||||
params: {
|
||||
q: query,
|
||||
count: count,
|
||||
extra_snippets: true
|
||||
},
|
||||
headers: {
|
||||
'X-Subscription-Token': process.env.BRAVE_API_KEY,
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
timeout: 15000
|
||||
});
|
||||
|
||||
const results = response.data.web?.results || [];
|
||||
|
||||
if (results.length === 0) {
|
||||
return 'No search results found.';
|
||||
}
|
||||
|
||||
let formatted = 'Search results:\n\n';
|
||||
results.slice(0, 8).forEach((result, i) => {
|
||||
formatted += `${i + 1}. ${result.title}\n`;
|
||||
formatted += ` ${result.url}\n`;
|
||||
if (result.description) {
|
||||
formatted += ` ${result.description.substring(0, 200)}${result.description.length > 200 ? '...' : ''}\n`;
|
||||
}
|
||||
if (result.extra_snippets && result.extra_snippets.length > 0) {
|
||||
formatted += ` ${result.extra_snippets[0].substring(0, 150)}...\n`;
|
||||
}
|
||||
formatted += '\n';
|
||||
});
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
// Tool definitions for function calling
|
||||
const WEB_SEARCH_TOOL = {
|
||||
type: 'function',
|
||||
@@ -77,7 +116,15 @@ function buildMessages(history, prompt, mentionedUsername, systemPrompt) {
|
||||
return messages;
|
||||
}
|
||||
|
||||
async function performWebSearch(query) {
|
||||
// Primary: Brave Search
|
||||
async function webSearchWithBrave(query) {
|
||||
console.log('Using Brave Search for:', query);
|
||||
return await performBraveSearch(query);
|
||||
}
|
||||
|
||||
// Fallback: NanoGPT web search
|
||||
async function webSearchWithNanoGPT(query) {
|
||||
console.log('Using NanoGPT web search for:', query);
|
||||
const response = await axios.post(
|
||||
'https://nano-gpt.com/api/web',
|
||||
{
|
||||
@@ -96,31 +143,52 @@ async function performWebSearch(query) {
|
||||
);
|
||||
|
||||
const data = response.data.data;
|
||||
const metadata = response.data.metadata;
|
||||
|
||||
// Format the response nicely
|
||||
let formattedResult = '';
|
||||
|
||||
if (typeof data === 'string') {
|
||||
formattedResult = data;
|
||||
return data;
|
||||
} else if (data.answer) {
|
||||
formattedResult = data.answer;
|
||||
let result = data.answer;
|
||||
if (data.sources && data.sources.length > 0) {
|
||||
formattedResult += '\n\nSources:\n';
|
||||
result += '\n\nSources:\n';
|
||||
data.sources.forEach((source, i) => {
|
||||
formattedResult += `${i + 1}. ${source.name || source.url}\n`;
|
||||
result += `${i + 1}. ${source.name || source.url}\n`;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
} else if (data.results) {
|
||||
formattedResult = 'Search results:\n';
|
||||
data.results.slice(0, 5).forEach((result, i) => {
|
||||
formattedResult += `${i + 1}. ${result.title || result.name}\n`;
|
||||
if (result.url) formattedResult += ` ${result.url}\n`;
|
||||
if (result.content) formattedResult += ` ${result.content.substring(0, 200)}...\n`;
|
||||
let result = 'Search results:\n';
|
||||
data.results.slice(0, 8).forEach((r, i) => {
|
||||
result += `${i + 1}. ${r.title || r.name}\n`;
|
||||
if (r.url) result += ` ${r.url}\n`;
|
||||
if (r.content) result += ` ${r.content.substring(0, 200)}...\n`;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
return formattedResult;
|
||||
return 'Could not parse search results.';
|
||||
}
|
||||
|
||||
// Unified web search function (Brave first, NanoGPT fallback)
|
||||
async function performWebSearch(query) {
|
||||
// Try Brave first if API key is available
|
||||
if (process.env.BRAVE_API_KEY) {
|
||||
try {
|
||||
return await webSearchWithBrave(query);
|
||||
} catch (error) {
|
||||
console.error('Brave Search failed:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to NanoGPT
|
||||
if (process.env.NANOGPT_API_KEY) {
|
||||
try {
|
||||
return await webSearchWithNanoGPT(query);
|
||||
} catch (error) {
|
||||
console.error('NanoGPT search failed:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('No web search provider available');
|
||||
}
|
||||
|
||||
async function callNanoGPTWithTools(messages, model, maxRetries = 2) {
|
||||
@@ -156,7 +224,7 @@ async function callNanoGPTWithTools(messages, model, maxRetries = 2) {
|
||||
const args = JSON.parse(toolCall.function.arguments);
|
||||
console.log('AI requested web search for:', args.query);
|
||||
|
||||
// Perform the search
|
||||
// Perform the search (Brave primary, NanoGPT fallback)
|
||||
const searchResult = await performWebSearch(args.query);
|
||||
|
||||
// Add tool result to messages and continue
|
||||
|
||||
Reference in New Issue
Block a user