feat(search): Add searchUrl to message

This commit is contained in:
Willie Zutz 2025-05-07 23:35:07 -06:00
parent 641968cf6a
commit bdace57cfd
5 changed files with 51 additions and 10 deletions

View file

@ -66,6 +66,7 @@ const handleEmitterEvents = async (
let recievedMessage = ''; let recievedMessage = '';
let sources: any[] = []; let sources: any[] = [];
let searchQuery: string | undefined; let searchQuery: string | undefined;
let searchUrl: string | undefined;
stream.on('data', (data) => { stream.on('data', (data) => {
const parsedData = JSON.parse(data); const parsedData = JSON.parse(data);
@ -86,6 +87,9 @@ const handleEmitterEvents = async (
if (parsedData.searchQuery) { if (parsedData.searchQuery) {
searchQuery = parsedData.searchQuery; searchQuery = parsedData.searchQuery;
} }
if (parsedData.searchUrl) {
searchUrl = parsedData.searchUrl;
}
writer.write( writer.write(
encoder.encode( encoder.encode(
@ -94,6 +98,7 @@ const handleEmitterEvents = async (
data: parsedData.data, data: parsedData.data,
searchQuery: parsedData.searchQuery, searchQuery: parsedData.searchQuery,
messageId: aiMessageId, messageId: aiMessageId,
searchUrl: searchUrl,
}) + '\n', }) + '\n',
), ),
); );
@ -128,6 +133,7 @@ const handleEmitterEvents = async (
messageId: aiMessageId, messageId: aiMessageId,
modelStats: modelStats, modelStats: modelStats,
searchQuery: searchQuery, searchQuery: searchQuery,
searchUrl: searchUrl,
}) + '\n', }) + '\n',
), ),
); );
@ -144,6 +150,7 @@ const handleEmitterEvents = async (
...(sources && sources.length > 0 && { sources }), ...(sources && sources.length > 0 && { sources }),
...(searchQuery && { searchQuery }), ...(searchQuery && { searchQuery }),
modelStats: modelStats, modelStats: modelStats,
...(searchUrl && { searchUrl }),
}), }),
}) })
.execute(); .execute();

View file

@ -28,6 +28,7 @@ export type Message = {
sources?: Document[]; sources?: Document[];
modelStats?: ModelStats; modelStats?: ModelStats;
searchQuery?: string; searchQuery?: string;
searchUrl?: string;
}; };
export interface File { export interface File {
@ -417,7 +418,6 @@ const ChatWindow = ({ id }: { id?: string }) => {
if (data.type === 'sources') { if (data.type === 'sources') {
sources = data.data; sources = data.data;
const searchQuery = data.searchQuery;
if (!added) { if (!added) {
setMessages((prevMessages) => [ setMessages((prevMessages) => [
...prevMessages, ...prevMessages,
@ -427,7 +427,8 @@ const ChatWindow = ({ id }: { id?: string }) => {
chatId: chatId!, chatId: chatId!,
role: 'assistant', role: 'assistant',
sources: sources, sources: sources,
searchQuery: searchQuery, searchQuery: data.searchQuery,
searchUrl: data.searchUrl,
createdAt: new Date(), createdAt: new Date(),
}, },
]); ]);
@ -486,6 +487,7 @@ const ChatWindow = ({ id }: { id?: string }) => {
modelStats: data.modelStats || null, modelStats: data.modelStats || null,
// Make sure the searchQuery is preserved (if available in the message data) // Make sure the searchQuery is preserved (if available in the message data)
searchQuery: message.searchQuery || data.searchQuery, searchQuery: message.searchQuery || data.searchQuery,
searchUrl: message.searchUrl || data.searchUrl,
}; };
} }
return message; return message;

View file

@ -283,9 +283,20 @@ const MessageBox = ({
<span className="font-medium text-black/70 dark:text-white/70"> <span className="font-medium text-black/70 dark:text-white/70">
Search query: Search query:
</span>{' '} </span>{' '}
<span className="text-black dark:text-white"> {message.searchUrl ? (
{message.searchQuery} <a
</span> href={message.searchUrl}
target="_blank"
rel="noopener noreferrer"
className="dark:text-white text-black hover:underline"
>
{message.searchQuery}
</a>
) : (
<span className="text-black dark:text-white">
{message.searchQuery}
</span>
)}
</div> </div>
)} )}
<MessageSources sources={message.sources} /> <MessageSources sources={message.sources} />

View file

@ -56,6 +56,7 @@ class MetaSearchAgent implements MetaSearchAgentType {
private config: Config; private config: Config;
private strParser = new StringOutputParser(); private strParser = new StringOutputParser();
private searchQuery?: string; private searchQuery?: string;
private searxngUrl?: string;
constructor(config: Config) { constructor(config: Config) {
this.config = config; this.config = config;
@ -81,6 +82,7 @@ class MetaSearchAgent implements MetaSearchAgentType {
let question = this.config.summarizer let question = this.config.summarizer
? await questionOutputParser.parse(input) ? await questionOutputParser.parse(input)
: input; : input;
console.log('question', question);
if (question === 'not_needed') { if (question === 'not_needed') {
return { query: '', docs: [] }; return { query: '', docs: [] };
@ -206,12 +208,15 @@ class MetaSearchAgent implements MetaSearchAgentType {
} else { } else {
question = question.replace(/<think>.*?<\/think>/g, ''); question = question.replace(/<think>.*?<\/think>/g, '');
const res = await searchSearxng(question, { const searxngResult = await searchSearxng(question, {
language: 'en', language: 'en',
engines: this.config.activeEngines, engines: this.config.activeEngines,
}); });
const documents = res.results.map( // Store the SearXNG URL for later use in emitting to the client
this.searxngUrl = searxngResult.searchUrl;
const documents = searxngResult.results.map(
(result) => (result) =>
new Document({ new Document({
pageContent: pageContent:
@ -447,9 +452,7 @@ class MetaSearchAgent implements MetaSearchAgentType {
event.event === 'on_chain_end' && event.event === 'on_chain_end' &&
event.name === 'FinalSourceRetriever' event.name === 'FinalSourceRetriever'
) { ) {
// Add searchQuery to the sources data if it exists
const sourcesData = event.data.output; const sourcesData = event.data.output;
// @ts-ignore - we added searchQuery property
if (this.searchQuery) { if (this.searchQuery) {
emitter.emit( emitter.emit(
'data', 'data',
@ -457,6 +460,7 @@ class MetaSearchAgent implements MetaSearchAgentType {
type: 'sources', type: 'sources',
data: sourcesData, data: sourcesData,
searchQuery: this.searchQuery, searchQuery: this.searchQuery,
searchUrl: this.searxngUrl,
}), }),
); );
} else { } else {

View file

@ -19,6 +19,12 @@ interface SearxngSearchResult {
iframe_src?: string; iframe_src?: string;
} }
interface SearxngResponse {
results: SearxngSearchResult[];
suggestions: string[];
searchUrl: string;
}
export const searchSearxng = async ( export const searchSearxng = async (
query: string, query: string,
opts?: SearxngSearchOptions, opts?: SearxngSearchOptions,
@ -44,5 +50,16 @@ export const searchSearxng = async (
const results: SearxngSearchResult[] = res.data.results; const results: SearxngSearchResult[] = res.data.results;
const suggestions: string[] = res.data.suggestions; const suggestions: string[] = res.data.suggestions;
return { results, suggestions }; // Create a URL for viewing the search results in the SearXNG web interface
const searchUrl = new URL(searxngURL);
searchUrl.pathname = '/search';
searchUrl.searchParams.append('q', query);
if (opts?.engines?.length) {
searchUrl.searchParams.append('engines', opts.engines.join(','));
}
if (opts?.language) {
searchUrl.searchParams.append('language', opts.language);
}
return { results, suggestions, searchUrl: searchUrl.toString() };
}; };