Add Brave Search as primary web search with NanoGPT fallback
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:
2026-03-09 16:51:43 -04:00
parent a73a8901f2
commit 7ff95a3d7f
3 changed files with 86 additions and 16 deletions
+84 -16
View File
@@ -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