mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2025-08-03 06:18:44 +00:00
Merge branch 'master' of https://github.com/HadiCherkaoui/Perplexica into pr/658
This commit is contained in:
@ -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
|
|
||||||
|
|
@ -142,6 +142,7 @@ You can access Perplexica over your home network by following our networking gui
|
|||||||
|
|
||||||
## One-Click Deployment
|
## One-Click Deployment
|
||||||
|
|
||||||
|
[](https://usw.sealos.io/?openapp=system-template%3FtemplateName%3Dperplexica)
|
||||||
[](https://repocloud.io/details/?app_id=267)
|
[](https://repocloud.io/details/?app_id=267)
|
||||||
|
|
||||||
## Upcoming Features
|
## Upcoming Features
|
||||||
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -42,11 +42,9 @@
|
|||||||
"drizzle-orm": "^0.31.2",
|
"drizzle-orm": "^0.31.2",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
"keycloak-js": "^26.1.3",
|
|
||||||
"langchain": "^0.1.30",
|
"langchain": "^0.1.30",
|
||||||
"mammoth": "^1.8.0",
|
"mammoth": "^1.8.0",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"next-auth": "^4.24.11",
|
|
||||||
"pdf-parse": "^1.1.1",
|
"pdf-parse": "^1.1.1",
|
||||||
"winston": "^3.13.0",
|
"winston": "^3.13.0",
|
||||||
"ws": "^8.17.1",
|
"ws": "^8.17.1",
|
||||||
|
@ -13,6 +13,16 @@ import {
|
|||||||
getCustomOpenaiApiUrl,
|
getCustomOpenaiApiUrl,
|
||||||
getCustomOpenaiApiKey,
|
getCustomOpenaiApiKey,
|
||||||
getCustomOpenaiModelName,
|
getCustomOpenaiModelName,
|
||||||
|
getSearchEngineBackend,
|
||||||
|
getImageSearchEngineBackend,
|
||||||
|
getVideoSearchEngineBackend,
|
||||||
|
getNewsSearchEngineBackend,
|
||||||
|
getSearxngApiEndpoint,
|
||||||
|
getGoogleApiKey,
|
||||||
|
getGoogleCseId,
|
||||||
|
getBingSubscriptionKey,
|
||||||
|
getBraveApiKey,
|
||||||
|
getYacyJsonEndpoint,
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import logger from '../utils/logger';
|
import logger from '../utils/logger';
|
||||||
|
|
||||||
@ -61,6 +71,21 @@ router.get('/', async (_, res) => {
|
|||||||
config['customOpenaiApiKey'] = getCustomOpenaiApiKey();
|
config['customOpenaiApiKey'] = getCustomOpenaiApiKey();
|
||||||
config['customOpenaiModelName'] = getCustomOpenaiModelName();
|
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);
|
res.status(200).json(config);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(500).json({ message: 'An error has occurred.' });
|
res.status(500).json({ message: 'An error has occurred.' });
|
||||||
@ -94,6 +119,30 @@ router.post('/', async (req, res) => {
|
|||||||
MODEL_NAME: config.customOpenaiModelName,
|
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);
|
updateConfig(updatedConfig);
|
||||||
|
@ -23,6 +23,18 @@ interface SettingsType {
|
|||||||
customOpenaiApiKey: string;
|
customOpenaiApiKey: string;
|
||||||
customOpenaiApiUrl: string;
|
customOpenaiApiUrl: string;
|
||||||
customOpenaiModelName: 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> {
|
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||||
@ -112,6 +124,12 @@ const Page = () => {
|
|||||||
const [automaticImageSearch, setAutomaticImageSearch] = useState(false);
|
const [automaticImageSearch, setAutomaticImageSearch] = useState(false);
|
||||||
const [automaticVideoSearch, setAutomaticVideoSearch] = useState(false);
|
const [automaticVideoSearch, setAutomaticVideoSearch] = useState(false);
|
||||||
const [savingStates, setSavingStates] = useState<Record<string, boolean>>({});
|
const [savingStates, setSavingStates] = useState<Record<string, boolean>>({});
|
||||||
|
const [searchEngineBackends, setSearchEngineBackends] = useState({
|
||||||
|
search: '',
|
||||||
|
image: '',
|
||||||
|
video: '',
|
||||||
|
news: '',
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchConfig = async () => {
|
const fetchConfig = async () => {
|
||||||
@ -125,6 +143,16 @@ const Page = () => {
|
|||||||
const data = (await res.json()) as SettingsType;
|
const data = (await res.json()) as SettingsType;
|
||||||
setConfig(data);
|
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 chatModelProvidersKeys = Object.keys(data.chatModelProviders || {});
|
||||||
const embeddingModelProvidersKeys = Object.keys(
|
const embeddingModelProvidersKeys = Object.keys(
|
||||||
data.embeddingModelProviders || {},
|
data.embeddingModelProviders || {},
|
||||||
@ -331,6 +359,8 @@ const Page = () => {
|
|||||||
localStorage.setItem('embeddingModelProvider', value);
|
localStorage.setItem('embeddingModelProvider', value);
|
||||||
} else if (key === 'embeddingModel') {
|
} else if (key === 'embeddingModel') {
|
||||||
localStorage.setItem('embeddingModel', value);
|
localStorage.setItem('embeddingModel', value);
|
||||||
|
} else if (key === 'searchEngineBackends') {
|
||||||
|
localStorage.setItem('searchEngineBackends', value);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to save:', err);
|
console.error('Failed to save:', err);
|
||||||
@ -793,6 +823,234 @@ const Page = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SettingsSection>
|
</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>
|
</div>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
1088
ui/yarn.lock
1088
ui/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user