This commit is contained in:
ItzCrazyKns
2025-03-02 15:35:14 +05:30
8 changed files with 1418 additions and 1065 deletions

View File

@ -1,29 +0,0 @@
dn: ou=People,dc=dev,dc=local
objectClass: organizationalUnit
ou: People
# default password for test users is: hashedpassword
dn: uid=jsmith,ou=People,dc=dev,dc=local
changetype: add
objectClass: inetOrgPerson
description: John Smith from Accounting. John is the project
manager of the building project, so contact him with any qu
estions.
cn: John
sn: Smith
uid: jsmith
mail: john.smith@dev.local
userPassword: {SSHA}dzOCOhiRXNHe+C3iE3TwZvI6jIIvNxpx
dn: uid=sbrown,ou=People,dc=dev,dc=local
changetype: add
objectClass: inetOrgPerson
description: Sally Brown from engineering. Sally is responsibl
e for designing the blue prints and testing the structural int
egrity of the design.
cn: Sally
sn: Brown
uid: sbrown
mail: sally.brown@dev.local
userPassword: {SSHA}dzOCOhiRXNHe+C3iE3TwZvI6jIIvNxpx

View File

@ -142,6 +142,7 @@ You can access Perplexica over your home network by following our networking gui
## One-Click Deployment
[![Deploy to Sealos](https://raw.githubusercontent.com/labring-actions/templates/main/Deploy-on-Sealos.svg)](https://usw.sealos.io/?openapp=system-template%3FtemplateName%3Dperplexica)
[![Deploy to RepoCloud](https://d16t0pc4846x52.cloudfront.net/deploylobe.svg)](https://repocloud.io/details/?app_id=267)
## Upcoming Features

View File

@ -1,8 +0,0 @@
# Local Development
Apply example users to openldap so keycloak can use them later on.
Their default password is hashedpassword and their usernames are jsmith or sbrown
```shell
ldapadd -x -H ldap://localhost -D "cn=admin,dc=dev,dc=local" -w admin -f .dev/people.ldif
```

View File

@ -42,11 +42,9 @@
"drizzle-orm": "^0.31.2",
"express": "^4.19.2",
"html-to-text": "^9.0.5",
"keycloak-js": "^26.1.3",
"langchain": "^0.1.30",
"mammoth": "^1.8.0",
"multer": "^1.4.5-lts.1",
"next-auth": "^4.24.11",
"pdf-parse": "^1.1.1",
"winston": "^3.13.0",
"ws": "^8.17.1",

View File

@ -13,6 +13,16 @@ import {
getCustomOpenaiApiUrl,
getCustomOpenaiApiKey,
getCustomOpenaiModelName,
getSearchEngineBackend,
getImageSearchEngineBackend,
getVideoSearchEngineBackend,
getNewsSearchEngineBackend,
getSearxngApiEndpoint,
getGoogleApiKey,
getGoogleCseId,
getBingSubscriptionKey,
getBraveApiKey,
getYacyJsonEndpoint,
} from '../config';
import logger from '../utils/logger';
@ -61,6 +71,21 @@ router.get('/', async (_, res) => {
config['customOpenaiApiKey'] = getCustomOpenaiApiKey();
config['customOpenaiModelName'] = getCustomOpenaiModelName();
// Add search engine configuration
config['searchEngineBackends'] = {
search: getSearchEngineBackend(),
image: getImageSearchEngineBackend(),
video: getVideoSearchEngineBackend(),
news: getNewsSearchEngineBackend(),
};
config['searxngEndpoint'] = getSearxngApiEndpoint();
config['googleApiKey'] = getGoogleApiKey();
config['googleCseId'] = getGoogleCseId();
config['bingSubscriptionKey'] = getBingSubscriptionKey();
config['braveApiKey'] = getBraveApiKey();
config['yacyEndpoint'] = getYacyJsonEndpoint();
res.status(200).json(config);
} catch (err: any) {
res.status(500).json({ message: 'An error has occurred.' });
@ -94,6 +119,30 @@ router.post('/', async (req, res) => {
MODEL_NAME: config.customOpenaiModelName,
},
},
SEARCH_ENGINE_BACKENDS: config.searchEngineBackends ? {
SEARCH: config.searchEngineBackends.search,
IMAGE: config.searchEngineBackends.image,
VIDEO: config.searchEngineBackends.video,
NEWS: config.searchEngineBackends.news,
} : undefined,
SEARCH_ENGINES: {
GOOGLE: {
API_KEY: config.googleApiKey,
CSE_ID: config.googleCseId,
},
SEARXNG: {
ENDPOINT: config.searxngEndpoint,
},
BING: {
SUBSCRIPTION_KEY: config.bingSubscriptionKey,
},
BRAVE: {
API_KEY: config.braveApiKey,
},
YACY: {
ENDPOINT: config.yacyEndpoint,
},
},
};
updateConfig(updatedConfig);

View File

@ -23,6 +23,18 @@ interface SettingsType {
customOpenaiApiKey: string;
customOpenaiApiUrl: string;
customOpenaiModelName: string;
searchEngineBackends: {
search: string;
image: string;
video: string;
news: string;
};
searxngEndpoint: string;
googleApiKey: string;
googleCseId: string;
bingSubscriptionKey: string;
braveApiKey: string;
yacyEndpoint: string;
}
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
@ -112,6 +124,12 @@ const Page = () => {
const [automaticImageSearch, setAutomaticImageSearch] = useState(false);
const [automaticVideoSearch, setAutomaticVideoSearch] = useState(false);
const [savingStates, setSavingStates] = useState<Record<string, boolean>>({});
const [searchEngineBackends, setSearchEngineBackends] = useState({
search: '',
image: '',
video: '',
news: '',
});
useEffect(() => {
const fetchConfig = async () => {
@ -125,6 +143,16 @@ const Page = () => {
const data = (await res.json()) as SettingsType;
setConfig(data);
// Set search engine backends if they exist in the response
if (data.searchEngineBackends) {
setSearchEngineBackends({
search: data.searchEngineBackends.search || '',
image: data.searchEngineBackends.image || '',
video: data.searchEngineBackends.video || '',
news: data.searchEngineBackends.news || '',
});
}
const chatModelProvidersKeys = Object.keys(data.chatModelProviders || {});
const embeddingModelProvidersKeys = Object.keys(
data.embeddingModelProviders || {},
@ -331,6 +359,8 @@ const Page = () => {
localStorage.setItem('embeddingModelProvider', value);
} else if (key === 'embeddingModel') {
localStorage.setItem('embeddingModel', value);
} else if (key === 'searchEngineBackends') {
localStorage.setItem('searchEngineBackends', value);
}
} catch (err) {
console.error('Failed to save:', err);
@ -793,6 +823,234 @@ const Page = () => {
</div>
</div>
</SettingsSection>
<SettingsSection title="Search Engine Settings">
<div className="flex flex-col space-y-4">
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
Default Search Engine
</p>
<Select
value={searchEngineBackends.search}
onChange={(e) => {
const value = e.target.value;
setSearchEngineBackends((prev) => ({
...prev,
search: value,
}));
saveConfig('searchEngineBackends', {
...searchEngineBackends,
search: value,
});
}}
options={[
{ value: 'searxng', label: 'SearXNG' },
{ value: 'google', label: 'Google' },
{ value: 'bing', label: 'Bing' },
{ value: 'brave', label: 'Brave' },
{ value: 'yacy', label: 'YaCy' },
]}
/>
</div>
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
Image Search Engine
</p>
<Select
value={searchEngineBackends.image}
onChange={(e) => {
const value = e.target.value;
setSearchEngineBackends((prev) => ({
...prev,
image: value,
}));
saveConfig('searchEngineBackends', {
...searchEngineBackends,
image: value,
});
}}
options={[
{ value: '', label: 'Use Default Search Engine' },
{ value: 'searxng', label: 'SearXNG' },
{ value: 'google', label: 'Google' },
{ value: 'bing', label: 'Bing' },
{ value: 'brave', label: 'Brave' },
]}
/>
</div>
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
Video Search Engine
</p>
<Select
value={searchEngineBackends.video}
onChange={(e) => {
const value = e.target.value;
setSearchEngineBackends((prev) => ({
...prev,
video: value,
}));
saveConfig('searchEngineBackends', {
...searchEngineBackends,
video: value,
});
}}
options={[
{ value: '', label: 'Use Default Search Engine' },
{ value: 'searxng', label: 'SearXNG' },
{ value: 'google', label: 'Google' },
{ value: 'bing', label: 'Bing' },
{ value: 'brave', label: 'Brave' },
]}
/>
</div>
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
News Search Engine
</p>
<Select
value={searchEngineBackends.news}
onChange={(e) => {
const value = e.target.value;
setSearchEngineBackends((prev) => ({
...prev,
news: value,
}));
saveConfig('searchEngineBackends', {
...searchEngineBackends,
news: value,
});
}}
options={[
{ value: '', label: 'Use Default Search Engine' },
{ value: 'searxng', label: 'SearXNG' },
{ value: 'google', label: 'Google' },
{ value: 'bing', label: 'Bing' },
{ value: 'brave', label: 'Brave' },
]}
/>
</div>
<div className="pt-4 border-t border-light-200 dark:border-dark-200">
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
SearXNG Endpoint
</p>
<Input
type="text"
placeholder="SearXNG API Endpoint"
value={config.searxngEndpoint || ''}
isSaving={savingStates['searxngEndpoint']}
onChange={(e) => {
setConfig((prev) => ({
...prev!,
searxngEndpoint: e.target.value,
}));
}}
onSave={(value) => saveConfig('searxngEndpoint', value)}
/>
</div>
</div>
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
Google API Key
</p>
<Input
type="text"
placeholder="Google API Key"
value={config.googleApiKey || ''}
isSaving={savingStates['googleApiKey']}
onChange={(e) => {
setConfig((prev) => ({
...prev!,
googleApiKey: e.target.value,
}));
}}
onSave={(value) => saveConfig('googleApiKey', value)}
/>
</div>
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
Google CSE ID
</p>
<Input
type="text"
placeholder="Google Custom Search Engine ID"
value={config.googleCseId || ''}
isSaving={savingStates['googleCseId']}
onChange={(e) => {
setConfig((prev) => ({
...prev!,
googleCseId: e.target.value,
}));
}}
onSave={(value) => saveConfig('googleCseId', value)}
/>
</div>
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
Bing Subscription Key
</p>
<Input
type="text"
placeholder="Bing Subscription Key"
value={config.bingSubscriptionKey || ''}
isSaving={savingStates['bingSubscriptionKey']}
onChange={(e) => {
setConfig((prev) => ({
...prev!,
bingSubscriptionKey: e.target.value,
}));
}}
onSave={(value) => saveConfig('bingSubscriptionKey', value)}
/>
</div>
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
Brave API Key
</p>
<Input
type="text"
placeholder="Brave API Key"
value={config.braveApiKey || ''}
isSaving={savingStates['braveApiKey']}
onChange={(e) => {
setConfig((prev) => ({
...prev!,
braveApiKey: e.target.value,
}));
}}
onSave={(value) => saveConfig('braveApiKey', value)}
/>
</div>
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
YaCy Endpoint
</p>
<Input
type="text"
placeholder="YaCy API Endpoint"
value={config.yacyEndpoint || ''}
isSaving={savingStates['yacyEndpoint']}
onChange={(e) => {
setConfig((prev) => ({
...prev!,
yacyEndpoint: e.target.value,
}));
}}
onSave={(value) => saveConfig('yacyEndpoint', value)}
/>
</div>
</div>
</SettingsSection>
</div>
)
)}

File diff suppressed because it is too large Load Diff

1048
yarn.lock

File diff suppressed because it is too large Load Diff