feat(search): Implement OpenSearch support

This commit is contained in:
Willie Zutz 2025-05-07 22:12:06 -06:00
parent e55f5ac941
commit 641968cf6a
6 changed files with 35 additions and 10 deletions

9
public/opensearch.xml Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Perplexica</ShortName>
<Description>Search with Perplexica AI</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16" type="image/x-icon">/favicon.ico</Image>
<Url type="text/html" template="/?q={searchTerms}"/>
<Url type="application/opensearchdescription+xml" rel="self" template="/opensearch.xml"/>
</OpenSearchDescription>

View file

@ -7,7 +7,7 @@ import db from '@/lib/db';
import { chats, messages as messagesSchema } from '@/lib/db/schema'; import { chats, messages as messagesSchema } from '@/lib/db/schema';
import { import {
getAvailableChatModelProviders, getAvailableChatModelProviders,
getAvailableEmbeddingModelProviders getAvailableEmbeddingModelProviders,
} from '@/lib/providers'; } from '@/lib/providers';
import { searchHandlers } from '@/lib/search'; import { searchHandlers } from '@/lib/search';
import { getFileDetails } from '@/lib/utils/files'; import { getFileDetails } from '@/lib/utils/files';

View file

@ -26,6 +26,14 @@ export default function RootLayout({
}>) { }>) {
return ( return (
<html className="h-full" lang="en" suppressHydrationWarning> <html className="h-full" lang="en" suppressHydrationWarning>
<head>
<link
rel="search"
type="application/opensearchdescription+xml"
title="Perplexica Search"
href="/opensearch.xml"
/>
</head>
<body className={cn('h-full', montserrat.className)}> <body className={cn('h-full', montserrat.className)}>
<ThemeProvider> <ThemeProvider>
<Sidebar>{children}</Sidebar> <Sidebar>{children}</Sidebar>

View file

@ -280,8 +280,12 @@ const MessageBox = ({
</div> </div>
{message.searchQuery && ( {message.searchQuery && (
<div className="mb-2 text-sm bg-light-secondary dark:bg-dark-secondary rounded-lg p-3"> <div className="mb-2 text-sm bg-light-secondary dark:bg-dark-secondary rounded-lg p-3">
<span className="font-medium text-black/70 dark:text-white/70">Search query:</span>{' '} <span className="font-medium text-black/70 dark:text-white/70">
<span className="text-black dark:text-white">{message.searchQuery}</span> Search query:
</span>{' '}
<span className="text-black dark:text-white">
{message.searchQuery}
</span>
</div> </div>
)} )}
<MessageSources sources={message.sources} /> <MessageSources sources={message.sources} />

View file

@ -86,7 +86,7 @@ const MessageInput = ({
setMessage(''); setMessage('');
}; };
return ( return (
<form <form
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
@ -107,7 +107,7 @@ const MessageInput = ({
onChange={(e) => setMessage(e.target.value)} onChange={(e) => setMessage(e.target.value)}
minRows={2} minRows={2}
className="bg-transparent placeholder:text-black/50 dark:placeholder:text-white/50 text-sm text-black dark:text-white resize-none focus:outline-none w-full max-h-24 lg:max-h-36 xl:max-h-48" className="bg-transparent placeholder:text-black/50 dark:placeholder:text-white/50 text-sm text-black dark:text-white resize-none focus:outline-none w-full max-h-24 lg:max-h-36 xl:max-h-48"
placeholder={firstMessage ? "Ask anything..." :"Ask a follow-up"} placeholder={firstMessage ? 'Ask anything...' : 'Ask a follow-up'}
/> />
<div className="flex flex-row items-center justify-between mt-4"> <div className="flex flex-row items-center justify-between mt-4">
<div className="flex flex-row items-center space-x-2 lg:space-x-4"> <div className="flex flex-row items-center space-x-2 lg:space-x-4">
@ -134,7 +134,11 @@ const MessageInput = ({
className="bg-[#24A0ED] text-white disabled:text-black/50 dark:disabled:text-white/50 disabled:bg-[#e0e0dc] dark:disabled:bg-[#ececec21] hover:bg-opacity-85 transition duration-100 rounded-full p-2" className="bg-[#24A0ED] text-white disabled:text-black/50 dark:disabled:text-white/50 disabled:bg-[#e0e0dc] dark:disabled:bg-[#ececec21] hover:bg-opacity-85 transition duration-100 rounded-full p-2"
type="submit" type="submit"
> >
{firstMessage ? <ArrowRight className="bg-background" size={17} /> : <ArrowUp className="bg-background" size={17} />} {firstMessage ? (
<ArrowRight className="bg-background" size={17} />
) : (
<ArrowUp className="bg-background" size={17} />
)}
</button> </button>
</div> </div>
</div> </div>

View file

@ -265,7 +265,7 @@ class MetaSearchAgent implements MetaSearchAgentType {
query = searchRetrieverResult.query; query = searchRetrieverResult.query;
docs = searchRetrieverResult.docs; docs = searchRetrieverResult.docs;
// Store the search query in the context for emitting to the client // Store the search query in the context for emitting to the client
if (searchRetrieverResult.searchQuery) { if (searchRetrieverResult.searchQuery) {
this.searchQuery = searchRetrieverResult.searchQuery; this.searchQuery = searchRetrieverResult.searchQuery;
@ -453,10 +453,10 @@ class MetaSearchAgent implements MetaSearchAgentType {
if (this.searchQuery) { if (this.searchQuery) {
emitter.emit( emitter.emit(
'data', 'data',
JSON.stringify({ JSON.stringify({
type: 'sources', type: 'sources',
data: sourcesData, data: sourcesData,
searchQuery: this.searchQuery searchQuery: this.searchQuery,
}), }),
); );
} else { } else {