Spaces:
Running
Running
Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .assets/perplexica-preview.gif +3 -0
- .assets/perplexica-screenshot.png +3 -0
- .dockerignore +1 -0
- .gitattributes +2 -0
- .github/ISSUE_TEMPLATE/bug_report.md +27 -0
- .github/ISSUE_TEMPLATE/custom.md +7 -0
- .github/ISSUE_TEMPLATE/feature_request.md +19 -0
- .github/workflows/docker-build.yaml +73 -0
- .gitignore +39 -0
- .prettierignore +41 -0
- .prettierrc.js +12 -0
- CONTRIBUTING.md +41 -0
- LICENSE +21 -0
- README.md +178 -10
- app.dockerfile +15 -0
- backend.dockerfile +17 -0
- data/.gitignore +2 -0
- docker-compose.yaml +54 -0
- docs/API/SEARCH.md +117 -0
- docs/architecture/README.md +11 -0
- docs/architecture/WORKING.md +19 -0
- docs/installation/NETWORKING.md +109 -0
- docs/installation/UPDATING.md +40 -0
- drizzle.config.ts +10 -0
- package.json +53 -0
- sample.config.toml +14 -0
- src/app.ts +38 -0
- src/chains/imageSearchAgent.ts +84 -0
- src/chains/suggestionGeneratorAgent.ts +55 -0
- src/chains/videoSearchAgent.ts +90 -0
- src/config.ts +79 -0
- src/db/index.ts +10 -0
- src/db/schema.ts +28 -0
- src/lib/huggingfaceTransformer.ts +82 -0
- src/lib/outputParsers/lineOutputParser.ts +48 -0
- src/lib/outputParsers/listLineOutputParser.ts +50 -0
- src/lib/providers/anthropic.ts +59 -0
- src/lib/providers/gemini.ts +69 -0
- src/lib/providers/groq.ts +136 -0
- src/lib/providers/index.ts +49 -0
- src/lib/providers/ollama.ts +74 -0
- src/lib/providers/openai.ts +89 -0
- src/lib/providers/transformers.ts +32 -0
- src/lib/searxng.ts +47 -0
- src/prompts/academicSearch.ts +65 -0
- src/prompts/index.ts +32 -0
- src/prompts/redditSearch.ts +65 -0
- src/prompts/webSearch.ts +106 -0
- src/prompts/wolframAlpha.ts +65 -0
- src/prompts/writingAssistant.ts +13 -0
.assets/perplexica-preview.gif
ADDED
![]() |
Git LFS Details
|
.assets/perplexica-screenshot.png
ADDED
![]() |
Git LFS Details
|
.dockerignore
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
**/node_modules
|
.gitattributes
CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
.assets/perplexica-preview.gif filter=lfs diff=lfs merge=lfs -text
|
37 |
+
.assets/perplexica-screenshot.png filter=lfs diff=lfs merge=lfs -text
|
.github/ISSUE_TEMPLATE/bug_report.md
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
name: Bug report
|
3 |
+
about: Create an issue to help us fix bugs
|
4 |
+
title: ''
|
5 |
+
labels: bug
|
6 |
+
assignees: ''
|
7 |
+
---
|
8 |
+
|
9 |
+
**Describe the bug**
|
10 |
+
A clear and concise description of what the bug is.
|
11 |
+
|
12 |
+
**To Reproduce**
|
13 |
+
Steps to reproduce the behavior:
|
14 |
+
|
15 |
+
1. Go to '...'
|
16 |
+
2. Click on '....'
|
17 |
+
3. Scroll down to '....'
|
18 |
+
4. See error
|
19 |
+
|
20 |
+
**Expected behavior**
|
21 |
+
A clear and concise description of what you expected to happen.
|
22 |
+
|
23 |
+
**Screenshots**
|
24 |
+
If applicable, add screenshots to help explain your problem.
|
25 |
+
|
26 |
+
**Additional context**
|
27 |
+
Add any other context about the problem here.
|
.github/ISSUE_TEMPLATE/custom.md
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
name: Custom issue template
|
3 |
+
about: Describe this issue template's purpose here.
|
4 |
+
title: ''
|
5 |
+
labels: ''
|
6 |
+
assignees: ''
|
7 |
+
---
|
.github/ISSUE_TEMPLATE/feature_request.md
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
name: Feature request
|
3 |
+
about: Suggest an idea for this project
|
4 |
+
title: ''
|
5 |
+
labels: enhancement
|
6 |
+
assignees: ''
|
7 |
+
---
|
8 |
+
|
9 |
+
**Is your feature request related to a problem? Please describe.**
|
10 |
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
11 |
+
|
12 |
+
**Describe the solution you'd like**
|
13 |
+
A clear and concise description of what you want to happen.
|
14 |
+
|
15 |
+
**Describe alternatives you've considered**
|
16 |
+
A clear and concise description of any alternative solutions or features you've considered.
|
17 |
+
|
18 |
+
**Additional context**
|
19 |
+
Add any other context or screenshots about the feature request here.
|
.github/workflows/docker-build.yaml
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Build & Push Docker Images
|
2 |
+
|
3 |
+
on:
|
4 |
+
push:
|
5 |
+
branches:
|
6 |
+
- master
|
7 |
+
release:
|
8 |
+
types: [published]
|
9 |
+
|
10 |
+
jobs:
|
11 |
+
build-and-push:
|
12 |
+
runs-on: ubuntu-latest
|
13 |
+
strategy:
|
14 |
+
matrix:
|
15 |
+
service: [backend, app]
|
16 |
+
steps:
|
17 |
+
- name: Checkout code
|
18 |
+
uses: actions/checkout@v3
|
19 |
+
|
20 |
+
- name: Set up QEMU
|
21 |
+
uses: docker/setup-qemu-action@v2
|
22 |
+
|
23 |
+
- name: Set up Docker Buildx
|
24 |
+
uses: docker/setup-buildx-action@v2
|
25 |
+
with:
|
26 |
+
install: true
|
27 |
+
|
28 |
+
- name: Log in to DockerHub
|
29 |
+
uses: docker/login-action@v2
|
30 |
+
with:
|
31 |
+
username: ${{ secrets.DOCKER_USERNAME }}
|
32 |
+
password: ${{ secrets.DOCKER_PASSWORD }}
|
33 |
+
|
34 |
+
- name: Extract version from release tag
|
35 |
+
if: github.event_name == 'release'
|
36 |
+
id: version
|
37 |
+
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
38 |
+
|
39 |
+
- name: Build and push Docker image for ${{ matrix.service }}
|
40 |
+
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
41 |
+
run: |
|
42 |
+
docker buildx create --use
|
43 |
+
if [[ "${{ matrix.service }}" == "backend" ]]; then \
|
44 |
+
DOCKERFILE=backend.dockerfile; \
|
45 |
+
IMAGE_NAME=perplexica-backend; \
|
46 |
+
else \
|
47 |
+
DOCKERFILE=app.dockerfile; \
|
48 |
+
IMAGE_NAME=perplexica-frontend; \
|
49 |
+
fi
|
50 |
+
docker buildx build --platform linux/amd64,linux/arm64 \
|
51 |
+
--cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:main \
|
52 |
+
--cache-to=type=inline \
|
53 |
+
-f $DOCKERFILE \
|
54 |
+
-t itzcrazykns1337/${IMAGE_NAME}:main \
|
55 |
+
--push .
|
56 |
+
|
57 |
+
- name: Build and push release Docker image for ${{ matrix.service }}
|
58 |
+
if: github.event_name == 'release'
|
59 |
+
run: |
|
60 |
+
docker buildx create --use
|
61 |
+
if [[ "${{ matrix.service }}" == "backend" ]]; then \
|
62 |
+
DOCKERFILE=backend.dockerfile; \
|
63 |
+
IMAGE_NAME=perplexica-backend; \
|
64 |
+
else \
|
65 |
+
DOCKERFILE=app.dockerfile; \
|
66 |
+
IMAGE_NAME=perplexica-frontend; \
|
67 |
+
fi
|
68 |
+
docker buildx build --platform linux/amd64,linux/arm64 \
|
69 |
+
--cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }} \
|
70 |
+
--cache-to=type=inline \
|
71 |
+
-f $DOCKERFILE \
|
72 |
+
-t itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }} \
|
73 |
+
--push .
|
.gitignore
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Node.js
|
2 |
+
node_modules/
|
3 |
+
npm-debug.log
|
4 |
+
yarn-error.log
|
5 |
+
|
6 |
+
# Build output
|
7 |
+
/.next/
|
8 |
+
/out/
|
9 |
+
/dist/
|
10 |
+
|
11 |
+
# IDE/Editor specific
|
12 |
+
.vscode/
|
13 |
+
.idea/
|
14 |
+
*.iml
|
15 |
+
|
16 |
+
# Environment variables
|
17 |
+
.env
|
18 |
+
.env.local
|
19 |
+
.env.development.local
|
20 |
+
.env.test.local
|
21 |
+
.env.production.local
|
22 |
+
|
23 |
+
# Config files
|
24 |
+
config.toml
|
25 |
+
|
26 |
+
# Log files
|
27 |
+
logs/
|
28 |
+
*.log
|
29 |
+
|
30 |
+
# Testing
|
31 |
+
/coverage/
|
32 |
+
|
33 |
+
# Miscellaneous
|
34 |
+
.DS_Store
|
35 |
+
Thumbs.db
|
36 |
+
|
37 |
+
# Db
|
38 |
+
db.sqlite
|
39 |
+
/searxng
|
.prettierignore
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Ignore all files in the node_modules directory
|
2 |
+
node_modules
|
3 |
+
|
4 |
+
# Ignore all files in the .next directory (Next.js build output)
|
5 |
+
.next
|
6 |
+
|
7 |
+
# Ignore all files in the .out directory (TypeScript build output)
|
8 |
+
.out
|
9 |
+
|
10 |
+
# Ignore all files in the .cache directory (Prettier cache)
|
11 |
+
.cache
|
12 |
+
|
13 |
+
# Ignore all files in the .vscode directory (Visual Studio Code settings)
|
14 |
+
.vscode
|
15 |
+
|
16 |
+
# Ignore all files in the .idea directory (IntelliJ IDEA settings)
|
17 |
+
.idea
|
18 |
+
|
19 |
+
# Ignore all files in the dist directory (build output)
|
20 |
+
dist
|
21 |
+
|
22 |
+
# Ignore all files in the build directory (build output)
|
23 |
+
build
|
24 |
+
|
25 |
+
# Ignore all files in the coverage directory (test coverage reports)
|
26 |
+
coverage
|
27 |
+
|
28 |
+
# Ignore all files with the .log extension
|
29 |
+
*.log
|
30 |
+
|
31 |
+
# Ignore all files with the .tmp extension
|
32 |
+
*.tmp
|
33 |
+
|
34 |
+
# Ignore all files with the .swp extension
|
35 |
+
*.swp
|
36 |
+
|
37 |
+
# Ignore all files with the .DS_Store extension (macOS specific)
|
38 |
+
.DS_Store
|
39 |
+
|
40 |
+
# Ignore all files in uploads directory
|
41 |
+
uploads
|
.prettierrc.js
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** @type {import("prettier").Config} */
|
2 |
+
|
3 |
+
const config = {
|
4 |
+
printWidth: 80,
|
5 |
+
trailingComma: 'all',
|
6 |
+
endOfLine: 'auto',
|
7 |
+
singleQuote: true,
|
8 |
+
tabWidth: 2,
|
9 |
+
semi: true,
|
10 |
+
};
|
11 |
+
|
12 |
+
module.exports = config;
|
CONTRIBUTING.md
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# How to Contribute to Perplexica
|
2 |
+
|
3 |
+
Hey there, thanks for deciding to contribute to Perplexica. Anything you help with will support the development of Perplexica and will make it better. Let's walk you through the key aspects to ensure your contributions are effective and in harmony with the project's setup.
|
4 |
+
|
5 |
+
## Project Structure
|
6 |
+
|
7 |
+
Perplexica's design consists of two main domains:
|
8 |
+
|
9 |
+
- **Frontend (`ui` directory)**: This is a Next.js application holding all user interface components. It's a self-contained environment that manages everything the user interacts with.
|
10 |
+
- **Backend (root and `src` directory)**: The backend logic is situated in the `src` folder, but the root directory holds the main `package.json` for backend dependency management.
|
11 |
+
- All of the focus modes are created using the Meta Search Agent class present in `src/search/metaSearchAgent.ts`. The main logic behind Perplexica lies there.
|
12 |
+
|
13 |
+
## Setting Up Your Environment
|
14 |
+
|
15 |
+
Before diving into coding, setting up your local environment is key. Here's what you need to do:
|
16 |
+
|
17 |
+
### Backend
|
18 |
+
|
19 |
+
1. In the root directory, locate the `sample.config.toml` file.
|
20 |
+
2. Rename it to `config.toml` and fill in the necessary configuration fields specific to the backend.
|
21 |
+
3. Run `npm install` to install dependencies.
|
22 |
+
4. Run `npm run db:push` to set up the local sqlite.
|
23 |
+
5. Use `npm run dev` to start the backend in development mode.
|
24 |
+
|
25 |
+
### Frontend
|
26 |
+
|
27 |
+
1. Navigate to the `ui` folder and repeat the process of renaming `.env.example` to `.env`, making sure to provide the frontend-specific variables.
|
28 |
+
2. Execute `npm install` within the `ui` directory to get the frontend dependencies ready.
|
29 |
+
3. Launch the frontend development server with `npm run dev`.
|
30 |
+
|
31 |
+
**Please note**: Docker configurations are present for setting up production environments, whereas `npm run dev` is used for development purposes.
|
32 |
+
|
33 |
+
## Coding and Contribution Practices
|
34 |
+
|
35 |
+
Before committing changes:
|
36 |
+
|
37 |
+
1. Ensure that your code functions correctly by thorough testing.
|
38 |
+
2. Always run `npm run format:write` to format your code according to the project's coding standards. This helps maintain consistency and code quality.
|
39 |
+
3. We currently do not have a code of conduct, but it is in the works. In the meantime, please be mindful of how you engage with the project and its community.
|
40 |
+
|
41 |
+
Following these steps will help maintain the integrity of Perplexica's codebase and facilitate a smoother integration of your valuable contributions. Thank you for your support and commitment to improving Perplexica.
|
LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2024 ItzCrazyKns
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
README.md
CHANGED
@@ -1,10 +1,178 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 🚀 Perplexica - An AI-powered search engine 🔎 <!-- omit in toc -->
|
2 |
+
|
3 |
+
[](https://discord.gg/26aArMy8tT)
|
4 |
+
|
5 |
+
|
6 |
+

|
7 |
+
|
8 |
+
## Table of Contents <!-- omit in toc -->
|
9 |
+
|
10 |
+
- [Overview](#overview)
|
11 |
+
- [Preview](#preview)
|
12 |
+
- [Features](#features)
|
13 |
+
- [Installation](#installation)
|
14 |
+
- [Getting Started with Docker (Recommended)](#getting-started-with-docker-recommended)
|
15 |
+
- [Non-Docker Installation](#non-docker-installation)
|
16 |
+
- [Ollama Connection Errors](#ollama-connection-errors)
|
17 |
+
- [Using as a Search Engine](#using-as-a-search-engine)
|
18 |
+
- [Using Perplexica's API](#using-perplexicas-api)
|
19 |
+
- [Expose Perplexica to a network](#expose-perplexica-to-network)
|
20 |
+
- [One-Click Deployment](#one-click-deployment)
|
21 |
+
- [Upcoming Features](#upcoming-features)
|
22 |
+
- [Support Us](#support-us)
|
23 |
+
- [Donations](#donations)
|
24 |
+
- [Contribution](#contribution)
|
25 |
+
- [Help and Support](#help-and-support)
|
26 |
+
|
27 |
+
## Overview
|
28 |
+
|
29 |
+
Perplexica is an open-source AI-powered searching tool or an AI-powered search engine that goes deep into the internet to find answers. Inspired by Perplexity AI, it's an open-source option that not just searches the web but understands your questions. It uses advanced machine learning algorithms like similarity searching and embeddings to refine results and provides clear answers with sources cited.
|
30 |
+
|
31 |
+
Using SearxNG to stay current and fully open source, Perplexica ensures you always get the most up-to-date information without compromising your privacy.
|
32 |
+
|
33 |
+
Want to know more about its architecture and how it works? You can read it [here](https://github.com/ItzCrazyKns/Perplexica/tree/master/docs/architecture/README.md).
|
34 |
+
|
35 |
+
## Preview
|
36 |
+
|
37 |
+

|
38 |
+
|
39 |
+
## Features
|
40 |
+
|
41 |
+
- **Local LLMs**: You can make use local LLMs such as Llama3 and Mixtral using Ollama.
|
42 |
+
- **Two Main Modes:**
|
43 |
+
- **Copilot Mode:** (In development) Boosts search by generating different queries to find more relevant internet sources. Like normal search instead of just using the context by SearxNG, it visits the top matches and tries to find relevant sources to the user's query directly from the page.
|
44 |
+
- **Normal Mode:** Processes your query and performs a web search.
|
45 |
+
- **Focus Modes:** Special modes to better answer specific types of questions. Perplexica currently has 6 focus modes:
|
46 |
+
- **All Mode:** Searches the entire web to find the best results.
|
47 |
+
- **Writing Assistant Mode:** Helpful for writing tasks that does not require searching the web.
|
48 |
+
- **Academic Search Mode:** Finds articles and papers, ideal for academic research.
|
49 |
+
- **YouTube Search Mode:** Finds YouTube videos based on the search query.
|
50 |
+
- **Wolfram Alpha Search Mode:** Answers queries that need calculations or data analysis using Wolfram Alpha.
|
51 |
+
- **Reddit Search Mode:** Searches Reddit for discussions and opinions related to the query.
|
52 |
+
- **Current Information:** Some search tools might give you outdated info because they use data from crawling bots and convert them into embeddings and store them in a index. Unlike them, Perplexica uses SearxNG, a metasearch engine to get the results and rerank and get the most relevant source out of it, ensuring you always get the latest information without the overhead of daily data updates.
|
53 |
+
- **API**: Integrate Perplexica into your existing applications and make use of its capibilities.
|
54 |
+
|
55 |
+
It has many more features like image and video search. Some of the planned features are mentioned in [upcoming features](#upcoming-features).
|
56 |
+
|
57 |
+
## Installation
|
58 |
+
|
59 |
+
There are mainly 2 ways of installing Perplexica - With Docker, Without Docker. Using Docker is highly recommended.
|
60 |
+
|
61 |
+
### Getting Started with Docker (Recommended)
|
62 |
+
|
63 |
+
1. Ensure Docker is installed and running on your system.
|
64 |
+
2. Clone the Perplexica repository:
|
65 |
+
|
66 |
+
```bash
|
67 |
+
git clone https://github.com/ItzCrazyKns/Perplexica.git
|
68 |
+
```
|
69 |
+
|
70 |
+
3. After cloning, navigate to the directory containing the project files.
|
71 |
+
|
72 |
+
4. Rename the `sample.config.toml` file to `config.toml`. For Docker setups, you need only fill in the following fields:
|
73 |
+
|
74 |
+
- `OPENAI`: Your OpenAI API key. **You only need to fill this if you wish to use OpenAI's models**.
|
75 |
+
- `OLLAMA`: Your Ollama API URL. You should enter it as `http://host.docker.internal:PORT_NUMBER`. If you installed Ollama on port 11434, use `http://host.docker.internal:11434`. For other ports, adjust accordingly. **You need to fill this if you wish to use Ollama's models instead of OpenAI's**.
|
76 |
+
- `GROQ`: Your Groq API key. **You only need to fill this if you wish to use Groq's hosted models**.
|
77 |
+
- `ANTHROPIC`: Your Anthropic API key. **You only need to fill this if you wish to use Anthropic models**.
|
78 |
+
|
79 |
+
**Note**: You can change these after starting Perplexica from the settings dialog.
|
80 |
+
|
81 |
+
- `SIMILARITY_MEASURE`: The similarity measure to use (This is filled by default; you can leave it as is if you are unsure about it.)
|
82 |
+
|
83 |
+
5. Ensure you are in the directory containing the `docker-compose.yaml` file and execute:
|
84 |
+
|
85 |
+
```bash
|
86 |
+
docker compose up -d
|
87 |
+
```
|
88 |
+
|
89 |
+
6. Wait a few minutes for the setup to complete. You can access Perplexica at http://localhost:3000 in your web browser.
|
90 |
+
|
91 |
+
**Note**: After the containers are built, you can start Perplexica directly from Docker without having to open a terminal.
|
92 |
+
|
93 |
+
### Non-Docker Installation
|
94 |
+
|
95 |
+
1. Install SearXNG and allow `JSON` format in the SearXNG settings.
|
96 |
+
2. Clone the repository and rename the `sample.config.toml` file to `config.toml` in the root directory. Ensure you complete all required fields in this file.
|
97 |
+
3. Rename the `.env.example` file to `.env` in the `ui` folder and fill in all necessary fields.
|
98 |
+
4. After populating the configuration and environment files, run `npm i` in both the `ui` folder and the root directory.
|
99 |
+
5. Install the dependencies and then execute `npm run build` in both the `ui` folder and the root directory.
|
100 |
+
6. Finally, start both the frontend and the backend by running `npm run start` in both the `ui` folder and the root directory.
|
101 |
+
|
102 |
+
**Note**: Using Docker is recommended as it simplifies the setup process, especially for managing environment variables and dependencies.
|
103 |
+
|
104 |
+
See the [installation documentation](https://github.com/ItzCrazyKns/Perplexica/tree/master/docs/installation) for more information like exposing it your network, etc.
|
105 |
+
|
106 |
+
### Ollama Connection Errors
|
107 |
+
|
108 |
+
If you're encountering an Ollama connection error, it is likely due to the backend being unable to connect to Ollama's API. To fix this issue you can:
|
109 |
+
|
110 |
+
1. **Check your Ollama API URL:** Ensure that the API URL is correctly set in the settings menu.
|
111 |
+
2. **Update API URL Based on OS:**
|
112 |
+
|
113 |
+
- **Windows:** Use `http://host.docker.internal:11434`
|
114 |
+
- **Mac:** Use `http://host.docker.internal:11434`
|
115 |
+
- **Linux:** Use `http://<private_ip_of_host>:11434`
|
116 |
+
|
117 |
+
Adjust the port number if you're using a different one.
|
118 |
+
|
119 |
+
3. **Linux Users - Expose Ollama to Network:**
|
120 |
+
|
121 |
+
- Inside `/etc/systemd/system/ollama.service`, you need to add `Environment="OLLAMA_HOST=0.0.0.0"`. Then restart Ollama by `systemctl restart ollama`. For more information see [Ollama docs](https://github.com/ollama/ollama/blob/main/docs/faq.md#setting-environment-variables-on-linux)
|
122 |
+
|
123 |
+
- Ensure that the port (default is 11434) is not blocked by your firewall.
|
124 |
+
|
125 |
+
## Using as a Search Engine
|
126 |
+
|
127 |
+
If you wish to use Perplexica as an alternative to traditional search engines like Google or Bing, or if you want to add a shortcut for quick access from your browser's search bar, follow these steps:
|
128 |
+
|
129 |
+
1. Open your browser's settings.
|
130 |
+
2. Navigate to the 'Search Engines' section.
|
131 |
+
3. Add a new site search with the following URL: `http://localhost:3000/?q=%s`. Replace `localhost` with your IP address or domain name, and `3000` with the port number if Perplexica is not hosted locally.
|
132 |
+
4. Click the add button. Now, you can use Perplexica directly from your browser's search bar.
|
133 |
+
|
134 |
+
## Using Perplexica's API
|
135 |
+
|
136 |
+
Perplexica also provides an API for developers looking to integrate its powerful search engine into their own applications. You can run searches, use multiple models and get answers to your queries.
|
137 |
+
|
138 |
+
For more details, check out the full documentation [here](https://github.com/ItzCrazyKns/Perplexica/tree/master/docs/API/SEARCH.md).
|
139 |
+
|
140 |
+
## Expose Perplexica to network
|
141 |
+
|
142 |
+
You can access Perplexica over your home network by following our networking guide [here](https://github.com/ItzCrazyKns/Perplexica/blob/master/docs/installation/NETWORKING.md).
|
143 |
+
|
144 |
+
## One-Click Deployment
|
145 |
+
|
146 |
+
[](https://repocloud.io/details/?app_id=267)
|
147 |
+
|
148 |
+
## Upcoming Features
|
149 |
+
|
150 |
+
- [x] Add settings page
|
151 |
+
- [x] Adding support for local LLMs
|
152 |
+
- [x] History Saving features
|
153 |
+
- [x] Introducing various Focus Modes
|
154 |
+
- [x] Adding API support
|
155 |
+
- [x] Adding Discover
|
156 |
+
- [ ] Finalizing Copilot Mode
|
157 |
+
|
158 |
+
## Support Us
|
159 |
+
|
160 |
+
If you find Perplexica useful, consider giving us a star on GitHub. This helps more people discover Perplexica and supports the development of new features. Your support is greatly appreciated.
|
161 |
+
|
162 |
+
### Donations
|
163 |
+
|
164 |
+
We also accept donations to help sustain our project. If you would like to contribute, you can use the following options to donate. Thank you for your support!
|
165 |
+
|
166 |
+
| Ethereum |
|
167 |
+
| ----------------------------------------------------- |
|
168 |
+
| Address: `0xB025a84b2F269570Eb8D4b05DEdaA41D8525B6DD` |
|
169 |
+
|
170 |
+
## Contribution
|
171 |
+
|
172 |
+
Perplexica is built on the idea that AI and large language models should be easy for everyone to use. If you find bugs or have ideas, please share them in via GitHub Issues. For more information on contributing to Perplexica you can read the [CONTRIBUTING.md](CONTRIBUTING.md) file to learn more about Perplexica and how you can contribute to it.
|
173 |
+
|
174 |
+
## Help and Support
|
175 |
+
|
176 |
+
If you have any questions or feedback, please feel free to reach out to us. You can create an issue on GitHub or join our Discord server. There, you can connect with other users, share your experiences and reviews, and receive more personalized help. [Click here](https://discord.gg/EFwsmQDgAu) to join the Discord server. To discuss matters outside of regular support, feel free to contact me on Discord at `itzcrazykns`.
|
177 |
+
|
178 |
+
Thank you for exploring Perplexica, the AI-powered search engine designed to enhance your search experience. We are constantly working to improve Perplexica and expand its capabilities. We value your feedback and contributions which help us make Perplexica even better. Don't forget to check back for updates and new features!
|
app.dockerfile
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM node:20.18.0-alpine
|
2 |
+
|
3 |
+
ARG NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001
|
4 |
+
ARG NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api
|
5 |
+
ENV NEXT_PUBLIC_WS_URL=${NEXT_PUBLIC_WS_URL}
|
6 |
+
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
7 |
+
|
8 |
+
WORKDIR /home/perplexica
|
9 |
+
|
10 |
+
COPY ui /home/perplexica/
|
11 |
+
|
12 |
+
RUN yarn install --frozen-lockfile
|
13 |
+
RUN yarn build
|
14 |
+
|
15 |
+
CMD ["yarn", "start"]
|
backend.dockerfile
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM node:18-slim
|
2 |
+
|
3 |
+
WORKDIR /home/perplexica
|
4 |
+
|
5 |
+
COPY src /home/perplexica/src
|
6 |
+
COPY tsconfig.json /home/perplexica/
|
7 |
+
COPY drizzle.config.ts /home/perplexica/
|
8 |
+
COPY package.json /home/perplexica/
|
9 |
+
COPY yarn.lock /home/perplexica/
|
10 |
+
|
11 |
+
RUN mkdir /home/perplexica/data
|
12 |
+
RUN mkdir /home/perplexica/uploads
|
13 |
+
|
14 |
+
RUN yarn install --frozen-lockfile --network-timeout 600000
|
15 |
+
RUN yarn build
|
16 |
+
|
17 |
+
CMD ["yarn", "start"]
|
data/.gitignore
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
*
|
2 |
+
!.gitignore
|
docker-compose.yaml
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
services:
|
2 |
+
searxng:
|
3 |
+
image: docker.io/searxng/searxng:latest
|
4 |
+
volumes:
|
5 |
+
- ./searxng:/etc/searxng:rw
|
6 |
+
ports:
|
7 |
+
- 4000:8080
|
8 |
+
networks:
|
9 |
+
- perplexica-network
|
10 |
+
restart: unless-stopped
|
11 |
+
|
12 |
+
perplexica-backend:
|
13 |
+
build:
|
14 |
+
context: .
|
15 |
+
dockerfile: backend.dockerfile
|
16 |
+
image: itzcrazykns1337/perplexica-backend:main
|
17 |
+
environment:
|
18 |
+
- SEARXNG_API_URL=http://searxng:8080
|
19 |
+
depends_on:
|
20 |
+
- searxng
|
21 |
+
ports:
|
22 |
+
- 3001:3001
|
23 |
+
volumes:
|
24 |
+
- backend-dbstore:/home/perplexica/data
|
25 |
+
- uploads:/home/perplexica/uploads
|
26 |
+
- ./config.toml:/home/perplexica/config.toml
|
27 |
+
extra_hosts:
|
28 |
+
- 'host.docker.internal:host-gateway'
|
29 |
+
networks:
|
30 |
+
- perplexica-network
|
31 |
+
restart: unless-stopped
|
32 |
+
|
33 |
+
perplexica-frontend:
|
34 |
+
build:
|
35 |
+
context: .
|
36 |
+
dockerfile: app.dockerfile
|
37 |
+
args:
|
38 |
+
- NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api
|
39 |
+
- NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001
|
40 |
+
image: itzcrazykns1337/perplexica-frontend:main
|
41 |
+
depends_on:
|
42 |
+
- perplexica-backend
|
43 |
+
ports:
|
44 |
+
- 3000:3000
|
45 |
+
networks:
|
46 |
+
- perplexica-network
|
47 |
+
restart: unless-stopped
|
48 |
+
|
49 |
+
networks:
|
50 |
+
perplexica-network:
|
51 |
+
|
52 |
+
volumes:
|
53 |
+
backend-dbstore:
|
54 |
+
uploads:
|
docs/API/SEARCH.md
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Perplexica Search API Documentation
|
2 |
+
|
3 |
+
## Overview
|
4 |
+
|
5 |
+
Perplexica’s Search API makes it easy to use our AI-powered search engine. You can run different types of searches, pick the models you want to use, and get the most recent info. Follow the following headings to learn more about Perplexica's search API.
|
6 |
+
|
7 |
+
## Endpoint
|
8 |
+
|
9 |
+
### **POST** `http://localhost:3001/api/search`
|
10 |
+
|
11 |
+
**Note**: Replace `3001` with any other port if you've changed the default PORT
|
12 |
+
|
13 |
+
### Request
|
14 |
+
|
15 |
+
The API accepts a JSON object in the request body, where you define the focus mode, chat models, embedding models, and your query.
|
16 |
+
|
17 |
+
#### Request Body Structure
|
18 |
+
|
19 |
+
```json
|
20 |
+
{
|
21 |
+
"chatModel": {
|
22 |
+
"provider": "openai",
|
23 |
+
"model": "gpt-4o-mini"
|
24 |
+
},
|
25 |
+
"embeddingModel": {
|
26 |
+
"provider": "openai",
|
27 |
+
"model": "text-embedding-3-large"
|
28 |
+
},
|
29 |
+
"optimizationMode": "speed",
|
30 |
+
"focusMode": "webSearch",
|
31 |
+
"query": "What is Perplexica",
|
32 |
+
"history": [
|
33 |
+
["human", "Hi, how are you?"],
|
34 |
+
["assistant", "I am doing well, how can I help you today?"]
|
35 |
+
]
|
36 |
+
}
|
37 |
+
```
|
38 |
+
|
39 |
+
### Request Parameters
|
40 |
+
|
41 |
+
- **`chatModel`** (object, optional): Defines the chat model to be used for the query. For model details you can send a GET request at `http://localhost:3001/api/models`. Make sure to use the key value (For example "gpt-4o-mini" instead of the display name "GPT 4 omni mini").
|
42 |
+
|
43 |
+
- `provider`: Specifies the provider for the chat model (e.g., `openai`, `ollama`).
|
44 |
+
- `model`: The specific model from the chosen provider (e.g., `gpt-4o-mini`).
|
45 |
+
- Optional fields for custom OpenAI configuration:
|
46 |
+
- `customOpenAIBaseURL`: If you’re using a custom OpenAI instance, provide the base URL.
|
47 |
+
- `customOpenAIKey`: The API key for a custom OpenAI instance.
|
48 |
+
|
49 |
+
- **`embeddingModel`** (object, optional): Defines the embedding model for similarity-based searching. For model details you can send a GET request at `http://localhost:3001/api/models`. Make sure to use the key value (For example "text-embedding-3-large" instead of the display name "Text Embedding 3 Large").
|
50 |
+
|
51 |
+
- `provider`: The provider for the embedding model (e.g., `openai`).
|
52 |
+
- `model`: The specific embedding model (e.g., `text-embedding-3-large`).
|
53 |
+
|
54 |
+
- **`focusMode`** (string, required): Specifies which focus mode to use. Available modes:
|
55 |
+
|
56 |
+
- `webSearch`, `academicSearch`, `writingAssistant`, `wolframAlphaSearch`, `youtubeSearch`, `redditSearch`.
|
57 |
+
|
58 |
+
- **`optimizationMode`** (string, optional): Specifies the optimization mode to control the balance between performance and quality. Available modes:
|
59 |
+
|
60 |
+
- `speed`: Prioritize speed and return the fastest answer.
|
61 |
+
- `balanced`: Provide a balanced answer with good speed and reasonable quality.
|
62 |
+
|
63 |
+
- **`query`** (string, required): The search query or question.
|
64 |
+
|
65 |
+
- **`history`** (array, optional): An array of message pairs representing the conversation history. Each pair consists of a role (either 'human' or 'assistant') and the message content. This allows the system to use the context of the conversation to refine results. Example:
|
66 |
+
|
67 |
+
```json
|
68 |
+
[
|
69 |
+
["human", "What is Perplexica?"],
|
70 |
+
["assistant", "Perplexica is an AI-powered search engine..."]
|
71 |
+
]
|
72 |
+
```
|
73 |
+
|
74 |
+
### Response
|
75 |
+
|
76 |
+
The response from the API includes both the final message and the sources used to generate that message.
|
77 |
+
|
78 |
+
#### Example Response
|
79 |
+
|
80 |
+
```json
|
81 |
+
{
|
82 |
+
"message": "Perplexica is an innovative, open-source AI-powered search engine designed to enhance the way users search for information online. Here are some key features and characteristics of Perplexica:\n\n- **AI-Powered Technology**: It utilizes advanced machine learning algorithms to not only retrieve information but also to understand the context and intent behind user queries, providing more relevant results [1][5].\n\n- **Open-Source**: Being open-source, Perplexica offers flexibility and transparency, allowing users to explore its functionalities without the constraints of proprietary software [3][10].",
|
83 |
+
"sources": [
|
84 |
+
{
|
85 |
+
"pageContent": "Perplexica is an innovative, open-source AI-powered search engine designed to enhance the way users search for information online.",
|
86 |
+
"metadata": {
|
87 |
+
"title": "What is Perplexica, and how does it function as an AI-powered search ...",
|
88 |
+
"url": "https://askai.glarity.app/search/What-is-Perplexica--and-how-does-it-function-as-an-AI-powered-search-engine"
|
89 |
+
}
|
90 |
+
},
|
91 |
+
{
|
92 |
+
"pageContent": "Perplexica is an open-source AI-powered search tool that dives deep into the internet to find precise answers.",
|
93 |
+
"metadata": {
|
94 |
+
"title": "Sahar Mor's Post",
|
95 |
+
"url": "https://www.linkedin.com/posts/sahar-mor_a-new-open-source-project-called-perplexica-activity-7204489745668694016-ncja"
|
96 |
+
}
|
97 |
+
}
|
98 |
+
....
|
99 |
+
]
|
100 |
+
}
|
101 |
+
```
|
102 |
+
|
103 |
+
### Fields in the Response
|
104 |
+
|
105 |
+
- **`message`** (string): The search result, generated based on the query and focus mode.
|
106 |
+
- **`sources`** (array): A list of sources that were used to generate the search result. Each source includes:
|
107 |
+
- `pageContent`: A snippet of the relevant content from the source.
|
108 |
+
- `metadata`: Metadata about the source, including:
|
109 |
+
- `title`: The title of the webpage.
|
110 |
+
- `url`: The URL of the webpage.
|
111 |
+
|
112 |
+
### Error Handling
|
113 |
+
|
114 |
+
If an error occurs during the search process, the API will return an appropriate error message with an HTTP status code.
|
115 |
+
|
116 |
+
- **400**: If the request is malformed or missing required fields (e.g., no focus mode or query).
|
117 |
+
- **500**: If an internal server error occurs during the search.
|
docs/architecture/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Perplexica's Architecture
|
2 |
+
|
3 |
+
Perplexica's architecture consists of the following key components:
|
4 |
+
|
5 |
+
1. **User Interface**: A web-based interface that allows users to interact with Perplexica for searching images, videos, and much more.
|
6 |
+
2. **Agent/Chains**: These components predict Perplexica's next actions, understand user queries, and decide whether a web search is necessary.
|
7 |
+
3. **SearXNG**: A metadata search engine used by Perplexica to search the web for sources.
|
8 |
+
4. **LLMs (Large Language Models)**: Utilized by agents and chains for tasks like understanding content, writing responses, and citing sources. Examples include Claude, GPTs, etc.
|
9 |
+
5. **Embedding Models**: To improve the accuracy of search results, embedding models re-rank the results using similarity search algorithms such as cosine similarity and dot product distance.
|
10 |
+
|
11 |
+
For a more detailed explanation of how these components work together, see [WORKING.md](https://github.com/ItzCrazyKns/Perplexica/tree/master/docs/architecture/WORKING.md).
|
docs/architecture/WORKING.md
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# How does Perplexica work?
|
2 |
+
|
3 |
+
Curious about how Perplexica works? Don't worry, we'll cover it here. Before we begin, make sure you've read about the architecture of Perplexica to ensure you understand what it's made up of. Haven't read it? You can read it [here](https://github.com/ItzCrazyKns/Perplexica/tree/master/docs/architecture/README.md).
|
4 |
+
|
5 |
+
We'll understand how Perplexica works by taking an example of a scenario where a user asks: "How does an A.C. work?". We'll break down the process into steps to make it easier to understand. The steps are as follows:
|
6 |
+
|
7 |
+
1. The message is sent via WS to the backend server where it invokes the chain. The chain will depend on your focus mode. For this example, let's assume we use the "webSearch" focus mode.
|
8 |
+
2. The chain is now invoked; first, the message is passed to another chain where it first predicts (using the chat history and the question) whether there is a need for sources and searching the web. If there is, it will generate a query (in accordance with the chat history) for searching the web that we'll take up later. If not, the chain will end there, and then the answer generator chain, also known as the response generator, will be started.
|
9 |
+
3. The query returned by the first chain is passed to SearXNG to search the web for information.
|
10 |
+
4. After the information is retrieved, it is based on keyword-based search. We then convert the information into embeddings and the query as well, then we perform a similarity search to find the most relevant sources to answer the query.
|
11 |
+
5. After all this is done, the sources are passed to the response generator. This chain takes all the chat history, the query, and the sources. It generates a response that is streamed to the UI.
|
12 |
+
|
13 |
+
## How are the answers cited?
|
14 |
+
|
15 |
+
The LLMs are prompted to do so. We've prompted them so well that they cite the answers themselves, and using some UI magic, we display it to the user.
|
16 |
+
|
17 |
+
## Image and Video Search
|
18 |
+
|
19 |
+
Image and video searches are conducted in a similar manner. A query is always generated first, then we search the web for images and videos that match the query. These results are then returned to the user.
|
docs/installation/NETWORKING.md
ADDED
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Expose Perplexica to a network
|
2 |
+
|
3 |
+
This guide will show you how to make Perplexica available over a network. Follow these steps to allow computers on the same network to interact with Perplexica. Choose the instructions that match the operating system you are using.
|
4 |
+
|
5 |
+
## Windows
|
6 |
+
|
7 |
+
1. Open PowerShell as Administrator
|
8 |
+
|
9 |
+
2. Navigate to the directory containing the `docker-compose.yaml` file
|
10 |
+
|
11 |
+
3. Stop and remove the existing Perplexica containers and images:
|
12 |
+
|
13 |
+
```bash
|
14 |
+
docker compose down --rmi all
|
15 |
+
```
|
16 |
+
|
17 |
+
4. Open the `docker-compose.yaml` file in a text editor like Notepad++
|
18 |
+
|
19 |
+
5. Replace `127.0.0.1` with the IP address of the server Perplexica is running on in these two lines:
|
20 |
+
|
21 |
+
```bash
|
22 |
+
args:
|
23 |
+
- NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api
|
24 |
+
- NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001
|
25 |
+
```
|
26 |
+
|
27 |
+
6. Save and close the `docker-compose.yaml` file
|
28 |
+
|
29 |
+
7. Rebuild and restart the Perplexica container:
|
30 |
+
|
31 |
+
```bash
|
32 |
+
docker compose up -d --build
|
33 |
+
```
|
34 |
+
|
35 |
+
## macOS
|
36 |
+
|
37 |
+
1. Open the Terminal application
|
38 |
+
|
39 |
+
2. Navigate to the directory with the `docker-compose.yaml` file:
|
40 |
+
|
41 |
+
```bash
|
42 |
+
cd /path/to/docker-compose.yaml
|
43 |
+
```
|
44 |
+
|
45 |
+
3. Stop and remove existing containers and images:
|
46 |
+
|
47 |
+
```bash
|
48 |
+
docker compose down --rmi all
|
49 |
+
```
|
50 |
+
|
51 |
+
4. Open `docker-compose.yaml` in a text editor like Sublime Text:
|
52 |
+
|
53 |
+
```bash
|
54 |
+
nano docker-compose.yaml
|
55 |
+
```
|
56 |
+
|
57 |
+
5. Replace `127.0.0.1` with the server IP in these lines:
|
58 |
+
|
59 |
+
```bash
|
60 |
+
args:
|
61 |
+
- NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api
|
62 |
+
- NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001
|
63 |
+
```
|
64 |
+
|
65 |
+
6. Save and exit the editor
|
66 |
+
|
67 |
+
7. Rebuild and restart Perplexica:
|
68 |
+
|
69 |
+
```bash
|
70 |
+
docker compose up -d --build
|
71 |
+
```
|
72 |
+
|
73 |
+
## Linux
|
74 |
+
|
75 |
+
1. Open the terminal
|
76 |
+
|
77 |
+
2. Navigate to the `docker-compose.yaml` directory:
|
78 |
+
|
79 |
+
```bash
|
80 |
+
cd /path/to/docker-compose.yaml
|
81 |
+
```
|
82 |
+
|
83 |
+
3. Stop and remove containers and images:
|
84 |
+
|
85 |
+
```bash
|
86 |
+
docker compose down --rmi all
|
87 |
+
```
|
88 |
+
|
89 |
+
4. Edit `docker-compose.yaml`:
|
90 |
+
|
91 |
+
```bash
|
92 |
+
nano docker-compose.yaml
|
93 |
+
```
|
94 |
+
|
95 |
+
5. Replace `127.0.0.1` with the server IP:
|
96 |
+
|
97 |
+
```bash
|
98 |
+
args:
|
99 |
+
- NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api
|
100 |
+
- NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001
|
101 |
+
```
|
102 |
+
|
103 |
+
6. Save and exit the editor
|
104 |
+
|
105 |
+
7. Rebuild and restart Perplexica:
|
106 |
+
|
107 |
+
```bash
|
108 |
+
docker compose up -d --build
|
109 |
+
```
|
docs/installation/UPDATING.md
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Update Perplexica to the latest version
|
2 |
+
|
3 |
+
To update Perplexica to the latest version, follow these steps:
|
4 |
+
|
5 |
+
## For Docker users
|
6 |
+
|
7 |
+
1. Clone the latest version of Perplexica from GitHub:
|
8 |
+
|
9 |
+
```bash
|
10 |
+
git clone https://github.com/ItzCrazyKns/Perplexica.git
|
11 |
+
```
|
12 |
+
|
13 |
+
2. Navigate to the Project Directory.
|
14 |
+
|
15 |
+
3. Pull latest images from registry.
|
16 |
+
|
17 |
+
```bash
|
18 |
+
docker compose pull
|
19 |
+
```
|
20 |
+
|
21 |
+
4. Update and Recreate containers.
|
22 |
+
|
23 |
+
```bash
|
24 |
+
docker compose up -d
|
25 |
+
```
|
26 |
+
|
27 |
+
5. Once the command completes running go to http://localhost:3000 and verify the latest changes.
|
28 |
+
|
29 |
+
## For non Docker users
|
30 |
+
|
31 |
+
1. Clone the latest version of Perplexica from GitHub:
|
32 |
+
|
33 |
+
```bash
|
34 |
+
git clone https://github.com/ItzCrazyKns/Perplexica.git
|
35 |
+
```
|
36 |
+
|
37 |
+
2. Navigate to the Project Directory
|
38 |
+
3. Execute `npm i` in both the `ui` folder and the root directory.
|
39 |
+
4. Once packages are updated, execute `npm run build` in both the `ui` folder and the root directory.
|
40 |
+
5. Finally, start both the frontend and the backend by running `npm run start` in both the `ui` folder and the root directory.
|
drizzle.config.ts
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { defineConfig } from 'drizzle-kit';
|
2 |
+
|
3 |
+
export default defineConfig({
|
4 |
+
dialect: 'sqlite',
|
5 |
+
schema: './src/db/schema.ts',
|
6 |
+
out: './drizzle',
|
7 |
+
dbCredentials: {
|
8 |
+
url: './data/db.sqlite',
|
9 |
+
},
|
10 |
+
});
|
package.json
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "perplexica-backend",
|
3 |
+
"version": "1.10.0-rc2",
|
4 |
+
"license": "MIT",
|
5 |
+
"author": "ItzCrazyKns",
|
6 |
+
"scripts": {
|
7 |
+
"start": "npm run db:push && node dist/app.js",
|
8 |
+
"build": "tsc",
|
9 |
+
"dev": "nodemon --ignore uploads/ src/app.ts ",
|
10 |
+
"db:push": "drizzle-kit push sqlite",
|
11 |
+
"format": "prettier . --check",
|
12 |
+
"format:write": "prettier . --write"
|
13 |
+
},
|
14 |
+
"devDependencies": {
|
15 |
+
"@types/better-sqlite3": "^7.6.10",
|
16 |
+
"@types/cors": "^2.8.17",
|
17 |
+
"@types/express": "^4.17.21",
|
18 |
+
"@types/html-to-text": "^9.0.4",
|
19 |
+
"@types/multer": "^1.4.12",
|
20 |
+
"@types/pdf-parse": "^1.1.4",
|
21 |
+
"@types/readable-stream": "^4.0.11",
|
22 |
+
"@types/ws": "^8.5.12",
|
23 |
+
"drizzle-kit": "^0.22.7",
|
24 |
+
"nodemon": "^3.1.0",
|
25 |
+
"prettier": "^3.2.5",
|
26 |
+
"ts-node": "^10.9.2",
|
27 |
+
"typescript": "^5.4.3"
|
28 |
+
},
|
29 |
+
"dependencies": {
|
30 |
+
"@iarna/toml": "^2.2.5",
|
31 |
+
"@langchain/anthropic": "^0.2.3",
|
32 |
+
"@langchain/community": "^0.2.16",
|
33 |
+
"@langchain/openai": "^0.0.25",
|
34 |
+
"@langchain/google-genai": "^0.0.23",
|
35 |
+
"@xenova/transformers": "^2.17.1",
|
36 |
+
"axios": "^1.6.8",
|
37 |
+
"better-sqlite3": "^11.0.0",
|
38 |
+
"compute-cosine-similarity": "^1.1.0",
|
39 |
+
"compute-dot": "^1.1.0",
|
40 |
+
"cors": "^2.8.5",
|
41 |
+
"dotenv": "^16.4.5",
|
42 |
+
"drizzle-orm": "^0.31.2",
|
43 |
+
"express": "^4.19.2",
|
44 |
+
"html-to-text": "^9.0.5",
|
45 |
+
"langchain": "^0.1.30",
|
46 |
+
"mammoth": "^1.8.0",
|
47 |
+
"multer": "^1.4.5-lts.1",
|
48 |
+
"pdf-parse": "^1.1.1",
|
49 |
+
"winston": "^3.13.0",
|
50 |
+
"ws": "^8.17.1",
|
51 |
+
"zod": "^3.22.4"
|
52 |
+
}
|
53 |
+
}
|
sample.config.toml
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[GENERAL]
|
2 |
+
PORT = 3001 # Port to run the server on
|
3 |
+
SIMILARITY_MEASURE = "cosine" # "cosine" or "dot"
|
4 |
+
KEEP_ALIVE = "5m" # How long to keep Ollama models loaded into memory. (Instead of using -1 use "-1m")
|
5 |
+
|
6 |
+
[API_KEYS]
|
7 |
+
OPENAI = "" # OpenAI API key - sk-1234567890abcdef1234567890abcdef
|
8 |
+
GROQ = "" # Groq API key - gsk_1234567890abcdef1234567890abcdef
|
9 |
+
ANTHROPIC = "" # Anthropic API key - sk-ant-1234567890abcdef1234567890abcdef
|
10 |
+
GEMINI = "" # Gemini API key - sk-1234567890abcdef1234567890abcdef
|
11 |
+
|
12 |
+
[API_ENDPOINTS]
|
13 |
+
SEARXNG = "http://localhost:32768" # SearxNG API URL
|
14 |
+
OLLAMA = "" # Ollama API URL - http://host.docker.internal:11434
|
src/app.ts
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { startWebSocketServer } from './websocket';
|
2 |
+
import express from 'express';
|
3 |
+
import cors from 'cors';
|
4 |
+
import http from 'http';
|
5 |
+
import routes from './routes';
|
6 |
+
import { getPort } from './config';
|
7 |
+
import logger from './utils/logger';
|
8 |
+
|
9 |
+
const port = getPort();
|
10 |
+
|
11 |
+
const app = express();
|
12 |
+
const server = http.createServer(app);
|
13 |
+
|
14 |
+
const corsOptions = {
|
15 |
+
origin: '*',
|
16 |
+
};
|
17 |
+
|
18 |
+
app.use(cors(corsOptions));
|
19 |
+
app.use(express.json());
|
20 |
+
|
21 |
+
app.use('/api', routes);
|
22 |
+
app.get('/api', (_, res) => {
|
23 |
+
res.status(200).json({ status: 'ok' });
|
24 |
+
});
|
25 |
+
|
26 |
+
server.listen(port, () => {
|
27 |
+
logger.info(`Server is running on port ${port}`);
|
28 |
+
});
|
29 |
+
|
30 |
+
startWebSocketServer(server);
|
31 |
+
|
32 |
+
process.on('uncaughtException', (err, origin) => {
|
33 |
+
logger.error(`Uncaught Exception at ${origin}: ${err}`);
|
34 |
+
});
|
35 |
+
|
36 |
+
process.on('unhandledRejection', (reason, promise) => {
|
37 |
+
logger.error(`Unhandled Rejection at: ${promise}, reason: ${reason}`);
|
38 |
+
});
|
src/chains/imageSearchAgent.ts
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
RunnableSequence,
|
3 |
+
RunnableMap,
|
4 |
+
RunnableLambda,
|
5 |
+
} from '@langchain/core/runnables';
|
6 |
+
import { PromptTemplate } from '@langchain/core/prompts';
|
7 |
+
import formatChatHistoryAsString from '../utils/formatHistory';
|
8 |
+
import { BaseMessage } from '@langchain/core/messages';
|
9 |
+
import { StringOutputParser } from '@langchain/core/output_parsers';
|
10 |
+
import { searchSearxng } from '../lib/searxng';
|
11 |
+
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
12 |
+
|
13 |
+
const imageSearchChainPrompt = `
|
14 |
+
You will be given a conversation below and a follow up question. You need to rephrase the follow-up question so it is a standalone question that can be used by the LLM to search the web for images.
|
15 |
+
You need to make sure the rephrased question agrees with the conversation and is relevant to the conversation.
|
16 |
+
|
17 |
+
Example:
|
18 |
+
1. Follow up question: What is a cat?
|
19 |
+
Rephrased: A cat
|
20 |
+
|
21 |
+
2. Follow up question: What is a car? How does it works?
|
22 |
+
Rephrased: Car working
|
23 |
+
|
24 |
+
3. Follow up question: How does an AC work?
|
25 |
+
Rephrased: AC working
|
26 |
+
|
27 |
+
Conversation:
|
28 |
+
{chat_history}
|
29 |
+
|
30 |
+
Follow up question: {query}
|
31 |
+
Rephrased question:
|
32 |
+
`;
|
33 |
+
|
34 |
+
type ImageSearchChainInput = {
|
35 |
+
chat_history: BaseMessage[];
|
36 |
+
query: string;
|
37 |
+
};
|
38 |
+
|
39 |
+
const strParser = new StringOutputParser();
|
40 |
+
|
41 |
+
const createImageSearchChain = (llm: BaseChatModel) => {
|
42 |
+
return RunnableSequence.from([
|
43 |
+
RunnableMap.from({
|
44 |
+
chat_history: (input: ImageSearchChainInput) => {
|
45 |
+
return formatChatHistoryAsString(input.chat_history);
|
46 |
+
},
|
47 |
+
query: (input: ImageSearchChainInput) => {
|
48 |
+
return input.query;
|
49 |
+
},
|
50 |
+
}),
|
51 |
+
PromptTemplate.fromTemplate(imageSearchChainPrompt),
|
52 |
+
llm,
|
53 |
+
strParser,
|
54 |
+
RunnableLambda.from(async (input: string) => {
|
55 |
+
const res = await searchSearxng(input, {
|
56 |
+
engines: ['bing images', 'google images'],
|
57 |
+
});
|
58 |
+
|
59 |
+
const images = [];
|
60 |
+
|
61 |
+
res.results.forEach((result) => {
|
62 |
+
if (result.img_src && result.url && result.title) {
|
63 |
+
images.push({
|
64 |
+
img_src: result.img_src,
|
65 |
+
url: result.url,
|
66 |
+
title: result.title,
|
67 |
+
});
|
68 |
+
}
|
69 |
+
});
|
70 |
+
|
71 |
+
return images.slice(0, 10);
|
72 |
+
}),
|
73 |
+
]);
|
74 |
+
};
|
75 |
+
|
76 |
+
const handleImageSearch = (
|
77 |
+
input: ImageSearchChainInput,
|
78 |
+
llm: BaseChatModel,
|
79 |
+
) => {
|
80 |
+
const imageSearchChain = createImageSearchChain(llm);
|
81 |
+
return imageSearchChain.invoke(input);
|
82 |
+
};
|
83 |
+
|
84 |
+
export default handleImageSearch;
|
src/chains/suggestionGeneratorAgent.ts
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { RunnableSequence, RunnableMap } from '@langchain/core/runnables';
|
2 |
+
import ListLineOutputParser from '../lib/outputParsers/listLineOutputParser';
|
3 |
+
import { PromptTemplate } from '@langchain/core/prompts';
|
4 |
+
import formatChatHistoryAsString from '../utils/formatHistory';
|
5 |
+
import { BaseMessage } from '@langchain/core/messages';
|
6 |
+
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
7 |
+
import { ChatOpenAI } from '@langchain/openai';
|
8 |
+
|
9 |
+
const suggestionGeneratorPrompt = `
|
10 |
+
You are an AI suggestion generator for an AI powered search engine. You will be given a conversation below. You need to generate 4-5 suggestions based on the conversation. The suggestion should be relevant to the conversation that can be used by the user to ask the chat model for more information.
|
11 |
+
You need to make sure the suggestions are relevant to the conversation and are helpful to the user. Keep a note that the user might use these suggestions to ask a chat model for more information.
|
12 |
+
Make sure the suggestions are medium in length and are informative and relevant to the conversation.
|
13 |
+
|
14 |
+
Provide these suggestions separated by newlines between the XML tags <suggestions> and </suggestions>. For example:
|
15 |
+
|
16 |
+
<suggestions>
|
17 |
+
Tell me more about SpaceX and their recent projects
|
18 |
+
What is the latest news on SpaceX?
|
19 |
+
Who is the CEO of SpaceX?
|
20 |
+
</suggestions>
|
21 |
+
|
22 |
+
Conversation:
|
23 |
+
{chat_history}
|
24 |
+
`;
|
25 |
+
|
26 |
+
type SuggestionGeneratorInput = {
|
27 |
+
chat_history: BaseMessage[];
|
28 |
+
};
|
29 |
+
|
30 |
+
const outputParser = new ListLineOutputParser({
|
31 |
+
key: 'suggestions',
|
32 |
+
});
|
33 |
+
|
34 |
+
const createSuggestionGeneratorChain = (llm: BaseChatModel) => {
|
35 |
+
return RunnableSequence.from([
|
36 |
+
RunnableMap.from({
|
37 |
+
chat_history: (input: SuggestionGeneratorInput) =>
|
38 |
+
formatChatHistoryAsString(input.chat_history),
|
39 |
+
}),
|
40 |
+
PromptTemplate.fromTemplate(suggestionGeneratorPrompt),
|
41 |
+
llm,
|
42 |
+
outputParser,
|
43 |
+
]);
|
44 |
+
};
|
45 |
+
|
46 |
+
const generateSuggestions = (
|
47 |
+
input: SuggestionGeneratorInput,
|
48 |
+
llm: BaseChatModel,
|
49 |
+
) => {
|
50 |
+
(llm as unknown as ChatOpenAI).temperature = 0;
|
51 |
+
const suggestionGeneratorChain = createSuggestionGeneratorChain(llm);
|
52 |
+
return suggestionGeneratorChain.invoke(input);
|
53 |
+
};
|
54 |
+
|
55 |
+
export default generateSuggestions;
|
src/chains/videoSearchAgent.ts
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
RunnableSequence,
|
3 |
+
RunnableMap,
|
4 |
+
RunnableLambda,
|
5 |
+
} from '@langchain/core/runnables';
|
6 |
+
import { PromptTemplate } from '@langchain/core/prompts';
|
7 |
+
import formatChatHistoryAsString from '../utils/formatHistory';
|
8 |
+
import { BaseMessage } from '@langchain/core/messages';
|
9 |
+
import { StringOutputParser } from '@langchain/core/output_parsers';
|
10 |
+
import { searchSearxng } from '../lib/searxng';
|
11 |
+
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
12 |
+
|
13 |
+
const VideoSearchChainPrompt = `
|
14 |
+
You will be given a conversation below and a follow up question. You need to rephrase the follow-up question so it is a standalone question that can be used by the LLM to search Youtube for videos.
|
15 |
+
You need to make sure the rephrased question agrees with the conversation and is relevant to the conversation.
|
16 |
+
|
17 |
+
Example:
|
18 |
+
1. Follow up question: How does a car work?
|
19 |
+
Rephrased: How does a car work?
|
20 |
+
|
21 |
+
2. Follow up question: What is the theory of relativity?
|
22 |
+
Rephrased: What is theory of relativity
|
23 |
+
|
24 |
+
3. Follow up question: How does an AC work?
|
25 |
+
Rephrased: How does an AC work
|
26 |
+
|
27 |
+
Conversation:
|
28 |
+
{chat_history}
|
29 |
+
|
30 |
+
Follow up question: {query}
|
31 |
+
Rephrased question:
|
32 |
+
`;
|
33 |
+
|
34 |
+
type VideoSearchChainInput = {
|
35 |
+
chat_history: BaseMessage[];
|
36 |
+
query: string;
|
37 |
+
};
|
38 |
+
|
39 |
+
const strParser = new StringOutputParser();
|
40 |
+
|
41 |
+
const createVideoSearchChain = (llm: BaseChatModel) => {
|
42 |
+
return RunnableSequence.from([
|
43 |
+
RunnableMap.from({
|
44 |
+
chat_history: (input: VideoSearchChainInput) => {
|
45 |
+
return formatChatHistoryAsString(input.chat_history);
|
46 |
+
},
|
47 |
+
query: (input: VideoSearchChainInput) => {
|
48 |
+
return input.query;
|
49 |
+
},
|
50 |
+
}),
|
51 |
+
PromptTemplate.fromTemplate(VideoSearchChainPrompt),
|
52 |
+
llm,
|
53 |
+
strParser,
|
54 |
+
RunnableLambda.from(async (input: string) => {
|
55 |
+
const res = await searchSearxng(input, {
|
56 |
+
engines: ['youtube'],
|
57 |
+
});
|
58 |
+
|
59 |
+
const videos = [];
|
60 |
+
|
61 |
+
res.results.forEach((result) => {
|
62 |
+
if (
|
63 |
+
result.thumbnail &&
|
64 |
+
result.url &&
|
65 |
+
result.title &&
|
66 |
+
result.iframe_src
|
67 |
+
) {
|
68 |
+
videos.push({
|
69 |
+
img_src: result.thumbnail,
|
70 |
+
url: result.url,
|
71 |
+
title: result.title,
|
72 |
+
iframe_src: result.iframe_src,
|
73 |
+
});
|
74 |
+
}
|
75 |
+
});
|
76 |
+
|
77 |
+
return videos.slice(0, 10);
|
78 |
+
}),
|
79 |
+
]);
|
80 |
+
};
|
81 |
+
|
82 |
+
const handleVideoSearch = (
|
83 |
+
input: VideoSearchChainInput,
|
84 |
+
llm: BaseChatModel,
|
85 |
+
) => {
|
86 |
+
const VideoSearchChain = createVideoSearchChain(llm);
|
87 |
+
return VideoSearchChain.invoke(input);
|
88 |
+
};
|
89 |
+
|
90 |
+
export default handleVideoSearch;
|
src/config.ts
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import fs from 'fs';
|
2 |
+
import path from 'path';
|
3 |
+
import toml from '@iarna/toml';
|
4 |
+
|
5 |
+
const configFileName = 'config.toml';
|
6 |
+
|
7 |
+
interface Config {
|
8 |
+
GENERAL: {
|
9 |
+
PORT: number;
|
10 |
+
SIMILARITY_MEASURE: string;
|
11 |
+
KEEP_ALIVE: string;
|
12 |
+
};
|
13 |
+
API_KEYS: {
|
14 |
+
OPENAI: string;
|
15 |
+
GROQ: string;
|
16 |
+
ANTHROPIC: string;
|
17 |
+
GEMINI: string;
|
18 |
+
};
|
19 |
+
API_ENDPOINTS: {
|
20 |
+
SEARXNG: string;
|
21 |
+
OLLAMA: string;
|
22 |
+
};
|
23 |
+
}
|
24 |
+
|
25 |
+
type RecursivePartial<T> = {
|
26 |
+
[P in keyof T]?: RecursivePartial<T[P]>;
|
27 |
+
};
|
28 |
+
|
29 |
+
const loadConfig = () =>
|
30 |
+
toml.parse(
|
31 |
+
fs.readFileSync(path.join(__dirname, `../${configFileName}`), 'utf-8'),
|
32 |
+
) as any as Config;
|
33 |
+
|
34 |
+
export const getPort = () => loadConfig().GENERAL.PORT;
|
35 |
+
|
36 |
+
export const getSimilarityMeasure = () =>
|
37 |
+
loadConfig().GENERAL.SIMILARITY_MEASURE;
|
38 |
+
|
39 |
+
export const getKeepAlive = () => loadConfig().GENERAL.KEEP_ALIVE;
|
40 |
+
|
41 |
+
export const getOpenaiApiKey = () => loadConfig().API_KEYS.OPENAI;
|
42 |
+
|
43 |
+
export const getGroqApiKey = () => loadConfig().API_KEYS.GROQ;
|
44 |
+
|
45 |
+
export const getAnthropicApiKey = () => loadConfig().API_KEYS.ANTHROPIC;
|
46 |
+
|
47 |
+
export const getGeminiApiKey = () => loadConfig().API_KEYS.GEMINI;
|
48 |
+
|
49 |
+
export const getSearxngApiEndpoint = () =>
|
50 |
+
process.env.SEARXNG_API_URL || loadConfig().API_ENDPOINTS.SEARXNG;
|
51 |
+
|
52 |
+
export const getOllamaApiEndpoint = () => loadConfig().API_ENDPOINTS.OLLAMA;
|
53 |
+
|
54 |
+
export const updateConfig = (config: RecursivePartial<Config>) => {
|
55 |
+
const currentConfig = loadConfig();
|
56 |
+
|
57 |
+
for (const key in currentConfig) {
|
58 |
+
if (!config[key]) config[key] = {};
|
59 |
+
|
60 |
+
if (typeof currentConfig[key] === 'object' && currentConfig[key] !== null) {
|
61 |
+
for (const nestedKey in currentConfig[key]) {
|
62 |
+
if (
|
63 |
+
!config[key][nestedKey] &&
|
64 |
+
currentConfig[key][nestedKey] &&
|
65 |
+
config[key][nestedKey] !== ''
|
66 |
+
) {
|
67 |
+
config[key][nestedKey] = currentConfig[key][nestedKey];
|
68 |
+
}
|
69 |
+
}
|
70 |
+
} else if (currentConfig[key] && config[key] !== '') {
|
71 |
+
config[key] = currentConfig[key];
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
fs.writeFileSync(
|
76 |
+
path.join(__dirname, `../${configFileName}`),
|
77 |
+
toml.stringify(config),
|
78 |
+
);
|
79 |
+
};
|
src/db/index.ts
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
2 |
+
import Database from 'better-sqlite3';
|
3 |
+
import * as schema from './schema';
|
4 |
+
|
5 |
+
const sqlite = new Database('data/db.sqlite');
|
6 |
+
const db = drizzle(sqlite, {
|
7 |
+
schema: schema,
|
8 |
+
});
|
9 |
+
|
10 |
+
export default db;
|
src/db/schema.ts
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { sql } from 'drizzle-orm';
|
2 |
+
import { text, integer, sqliteTable } from 'drizzle-orm/sqlite-core';
|
3 |
+
|
4 |
+
export const messages = sqliteTable('messages', {
|
5 |
+
id: integer('id').primaryKey(),
|
6 |
+
content: text('content').notNull(),
|
7 |
+
chatId: text('chatId').notNull(),
|
8 |
+
messageId: text('messageId').notNull(),
|
9 |
+
role: text('type', { enum: ['assistant', 'user'] }),
|
10 |
+
metadata: text('metadata', {
|
11 |
+
mode: 'json',
|
12 |
+
}),
|
13 |
+
});
|
14 |
+
|
15 |
+
interface File {
|
16 |
+
name: string;
|
17 |
+
fileId: string;
|
18 |
+
}
|
19 |
+
|
20 |
+
export const chats = sqliteTable('chats', {
|
21 |
+
id: text('id').primaryKey(),
|
22 |
+
title: text('title').notNull(),
|
23 |
+
createdAt: text('createdAt').notNull(),
|
24 |
+
focusMode: text('focusMode').notNull(),
|
25 |
+
files: text('files', { mode: 'json' })
|
26 |
+
.$type<File[]>()
|
27 |
+
.default(sql`'[]'`),
|
28 |
+
});
|
src/lib/huggingfaceTransformer.ts
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Embeddings, type EmbeddingsParams } from '@langchain/core/embeddings';
|
2 |
+
import { chunkArray } from '@langchain/core/utils/chunk_array';
|
3 |
+
|
4 |
+
export interface HuggingFaceTransformersEmbeddingsParams
|
5 |
+
extends EmbeddingsParams {
|
6 |
+
modelName: string;
|
7 |
+
|
8 |
+
model: string;
|
9 |
+
|
10 |
+
timeout?: number;
|
11 |
+
|
12 |
+
batchSize?: number;
|
13 |
+
|
14 |
+
stripNewLines?: boolean;
|
15 |
+
}
|
16 |
+
|
17 |
+
export class HuggingFaceTransformersEmbeddings
|
18 |
+
extends Embeddings
|
19 |
+
implements HuggingFaceTransformersEmbeddingsParams
|
20 |
+
{
|
21 |
+
modelName = 'Xenova/all-MiniLM-L6-v2';
|
22 |
+
|
23 |
+
model = 'Xenova/all-MiniLM-L6-v2';
|
24 |
+
|
25 |
+
batchSize = 512;
|
26 |
+
|
27 |
+
stripNewLines = true;
|
28 |
+
|
29 |
+
timeout?: number;
|
30 |
+
|
31 |
+
private pipelinePromise: Promise<any>;
|
32 |
+
|
33 |
+
constructor(fields?: Partial<HuggingFaceTransformersEmbeddingsParams>) {
|
34 |
+
super(fields ?? {});
|
35 |
+
|
36 |
+
this.modelName = fields?.model ?? fields?.modelName ?? this.model;
|
37 |
+
this.model = this.modelName;
|
38 |
+
this.stripNewLines = fields?.stripNewLines ?? this.stripNewLines;
|
39 |
+
this.timeout = fields?.timeout;
|
40 |
+
}
|
41 |
+
|
42 |
+
async embedDocuments(texts: string[]): Promise<number[][]> {
|
43 |
+
const batches = chunkArray(
|
44 |
+
this.stripNewLines ? texts.map((t) => t.replace(/\n/g, ' ')) : texts,
|
45 |
+
this.batchSize,
|
46 |
+
);
|
47 |
+
|
48 |
+
const batchRequests = batches.map((batch) => this.runEmbedding(batch));
|
49 |
+
const batchResponses = await Promise.all(batchRequests);
|
50 |
+
const embeddings: number[][] = [];
|
51 |
+
|
52 |
+
for (let i = 0; i < batchResponses.length; i += 1) {
|
53 |
+
const batchResponse = batchResponses[i];
|
54 |
+
for (let j = 0; j < batchResponse.length; j += 1) {
|
55 |
+
embeddings.push(batchResponse[j]);
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
return embeddings;
|
60 |
+
}
|
61 |
+
|
62 |
+
async embedQuery(text: string): Promise<number[]> {
|
63 |
+
const data = await this.runEmbedding([
|
64 |
+
this.stripNewLines ? text.replace(/\n/g, ' ') : text,
|
65 |
+
]);
|
66 |
+
return data[0];
|
67 |
+
}
|
68 |
+
|
69 |
+
private async runEmbedding(texts: string[]) {
|
70 |
+
const { pipeline } = await import('@xenova/transformers');
|
71 |
+
|
72 |
+
const pipe = await (this.pipelinePromise ??= pipeline(
|
73 |
+
'feature-extraction',
|
74 |
+
this.model,
|
75 |
+
));
|
76 |
+
|
77 |
+
return this.caller.call(async () => {
|
78 |
+
const output = await pipe(texts, { pooling: 'mean', normalize: true });
|
79 |
+
return output.tolist();
|
80 |
+
});
|
81 |
+
}
|
82 |
+
}
|
src/lib/outputParsers/lineOutputParser.ts
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { BaseOutputParser } from '@langchain/core/output_parsers';
|
2 |
+
|
3 |
+
interface LineOutputParserArgs {
|
4 |
+
key?: string;
|
5 |
+
}
|
6 |
+
|
7 |
+
class LineOutputParser extends BaseOutputParser<string> {
|
8 |
+
private key = 'questions';
|
9 |
+
|
10 |
+
constructor(args?: LineOutputParserArgs) {
|
11 |
+
super();
|
12 |
+
this.key = args.key ?? this.key;
|
13 |
+
}
|
14 |
+
|
15 |
+
static lc_name() {
|
16 |
+
return 'LineOutputParser';
|
17 |
+
}
|
18 |
+
|
19 |
+
lc_namespace = ['langchain', 'output_parsers', 'line_output_parser'];
|
20 |
+
|
21 |
+
async parse(text: string): Promise<string> {
|
22 |
+
text = text.trim() || '';
|
23 |
+
|
24 |
+
const regex = /^(\s*(-|\*|\d+\.\s|\d+\)\s|\u2022)\s*)+/;
|
25 |
+
const startKeyIndex = text.indexOf(`<${this.key}>`);
|
26 |
+
const endKeyIndex = text.indexOf(`</${this.key}>`);
|
27 |
+
|
28 |
+
if (startKeyIndex === -1 || endKeyIndex === -1) {
|
29 |
+
return '';
|
30 |
+
}
|
31 |
+
|
32 |
+
const questionsStartIndex =
|
33 |
+
startKeyIndex === -1 ? 0 : startKeyIndex + `<${this.key}>`.length;
|
34 |
+
const questionsEndIndex = endKeyIndex === -1 ? text.length : endKeyIndex;
|
35 |
+
const line = text
|
36 |
+
.slice(questionsStartIndex, questionsEndIndex)
|
37 |
+
.trim()
|
38 |
+
.replace(regex, '');
|
39 |
+
|
40 |
+
return line;
|
41 |
+
}
|
42 |
+
|
43 |
+
getFormatInstructions(): string {
|
44 |
+
throw new Error('Not implemented.');
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
export default LineOutputParser;
|
src/lib/outputParsers/listLineOutputParser.ts
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { BaseOutputParser } from '@langchain/core/output_parsers';
|
2 |
+
|
3 |
+
interface LineListOutputParserArgs {
|
4 |
+
key?: string;
|
5 |
+
}
|
6 |
+
|
7 |
+
class LineListOutputParser extends BaseOutputParser<string[]> {
|
8 |
+
private key = 'questions';
|
9 |
+
|
10 |
+
constructor(args?: LineListOutputParserArgs) {
|
11 |
+
super();
|
12 |
+
this.key = args.key ?? this.key;
|
13 |
+
}
|
14 |
+
|
15 |
+
static lc_name() {
|
16 |
+
return 'LineListOutputParser';
|
17 |
+
}
|
18 |
+
|
19 |
+
lc_namespace = ['langchain', 'output_parsers', 'line_list_output_parser'];
|
20 |
+
|
21 |
+
async parse(text: string): Promise<string[]> {
|
22 |
+
text = text.trim() || '';
|
23 |
+
|
24 |
+
const regex = /^(\s*(-|\*|\d+\.\s|\d+\)\s|\u2022)\s*)+/;
|
25 |
+
const startKeyIndex = text.indexOf(`<${this.key}>`);
|
26 |
+
const endKeyIndex = text.indexOf(`</${this.key}>`);
|
27 |
+
|
28 |
+
if (startKeyIndex === -1 || endKeyIndex === -1) {
|
29 |
+
return [];
|
30 |
+
}
|
31 |
+
|
32 |
+
const questionsStartIndex =
|
33 |
+
startKeyIndex === -1 ? 0 : startKeyIndex + `<${this.key}>`.length;
|
34 |
+
const questionsEndIndex = endKeyIndex === -1 ? text.length : endKeyIndex;
|
35 |
+
const lines = text
|
36 |
+
.slice(questionsStartIndex, questionsEndIndex)
|
37 |
+
.trim()
|
38 |
+
.split('\n')
|
39 |
+
.filter((line) => line.trim() !== '')
|
40 |
+
.map((line) => line.replace(regex, ''));
|
41 |
+
|
42 |
+
return lines;
|
43 |
+
}
|
44 |
+
|
45 |
+
getFormatInstructions(): string {
|
46 |
+
throw new Error('Not implemented.');
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
export default LineListOutputParser;
|
src/lib/providers/anthropic.ts
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ChatAnthropic } from '@langchain/anthropic';
|
2 |
+
import { getAnthropicApiKey } from '../../config';
|
3 |
+
import logger from '../../utils/logger';
|
4 |
+
|
5 |
+
export const loadAnthropicChatModels = async () => {
|
6 |
+
const anthropicApiKey = getAnthropicApiKey();
|
7 |
+
|
8 |
+
if (!anthropicApiKey) return {};
|
9 |
+
|
10 |
+
try {
|
11 |
+
const chatModels = {
|
12 |
+
'claude-3-5-sonnet-20241022': {
|
13 |
+
displayName: 'Claude 3.5 Sonnet',
|
14 |
+
model: new ChatAnthropic({
|
15 |
+
temperature: 0.7,
|
16 |
+
anthropicApiKey: anthropicApiKey,
|
17 |
+
model: 'claude-3-5-sonnet-20241022',
|
18 |
+
}),
|
19 |
+
},
|
20 |
+
'claude-3-5-haiku-20241022': {
|
21 |
+
displayName: 'Claude 3.5 Haiku',
|
22 |
+
model: new ChatAnthropic({
|
23 |
+
temperature: 0.7,
|
24 |
+
anthropicApiKey: anthropicApiKey,
|
25 |
+
model: 'claude-3-5-haiku-20241022',
|
26 |
+
}),
|
27 |
+
},
|
28 |
+
'claude-3-opus-20240229': {
|
29 |
+
displayName: 'Claude 3 Opus',
|
30 |
+
model: new ChatAnthropic({
|
31 |
+
temperature: 0.7,
|
32 |
+
anthropicApiKey: anthropicApiKey,
|
33 |
+
model: 'claude-3-opus-20240229',
|
34 |
+
}),
|
35 |
+
},
|
36 |
+
'claude-3-sonnet-20240229': {
|
37 |
+
displayName: 'Claude 3 Sonnet',
|
38 |
+
model: new ChatAnthropic({
|
39 |
+
temperature: 0.7,
|
40 |
+
anthropicApiKey: anthropicApiKey,
|
41 |
+
model: 'claude-3-sonnet-20240229',
|
42 |
+
}),
|
43 |
+
},
|
44 |
+
'claude-3-haiku-20240307': {
|
45 |
+
displayName: 'Claude 3 Haiku',
|
46 |
+
model: new ChatAnthropic({
|
47 |
+
temperature: 0.7,
|
48 |
+
anthropicApiKey: anthropicApiKey,
|
49 |
+
model: 'claude-3-haiku-20240307',
|
50 |
+
}),
|
51 |
+
},
|
52 |
+
};
|
53 |
+
|
54 |
+
return chatModels;
|
55 |
+
} catch (err) {
|
56 |
+
logger.error(`Error loading Anthropic models: ${err}`);
|
57 |
+
return {};
|
58 |
+
}
|
59 |
+
};
|
src/lib/providers/gemini.ts
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
ChatGoogleGenerativeAI,
|
3 |
+
GoogleGenerativeAIEmbeddings,
|
4 |
+
} from '@langchain/google-genai';
|
5 |
+
import { getGeminiApiKey } from '../../config';
|
6 |
+
import logger from '../../utils/logger';
|
7 |
+
|
8 |
+
export const loadGeminiChatModels = async () => {
|
9 |
+
const geminiApiKey = getGeminiApiKey();
|
10 |
+
|
11 |
+
if (!geminiApiKey) return {};
|
12 |
+
|
13 |
+
try {
|
14 |
+
const chatModels = {
|
15 |
+
'gemini-1.5-flash': {
|
16 |
+
displayName: 'Gemini 1.5 Flash',
|
17 |
+
model: new ChatGoogleGenerativeAI({
|
18 |
+
modelName: 'gemini-1.5-flash',
|
19 |
+
temperature: 0.7,
|
20 |
+
apiKey: geminiApiKey,
|
21 |
+
}),
|
22 |
+
},
|
23 |
+
'gemini-1.5-flash-8b': {
|
24 |
+
displayName: 'Gemini 1.5 Flash 8B',
|
25 |
+
model: new ChatGoogleGenerativeAI({
|
26 |
+
modelName: 'gemini-1.5-flash-8b',
|
27 |
+
temperature: 0.7,
|
28 |
+
apiKey: geminiApiKey,
|
29 |
+
}),
|
30 |
+
},
|
31 |
+
'gemini-1.5-pro': {
|
32 |
+
displayName: 'Gemini 1.5 Pro',
|
33 |
+
model: new ChatGoogleGenerativeAI({
|
34 |
+
modelName: 'gemini-1.5-pro',
|
35 |
+
temperature: 0.7,
|
36 |
+
apiKey: geminiApiKey,
|
37 |
+
}),
|
38 |
+
},
|
39 |
+
};
|
40 |
+
|
41 |
+
return chatModels;
|
42 |
+
} catch (err) {
|
43 |
+
logger.error(`Error loading Gemini models: ${err}`);
|
44 |
+
return {};
|
45 |
+
}
|
46 |
+
};
|
47 |
+
|
48 |
+
export const loadGeminiEmbeddingsModels = async () => {
|
49 |
+
const geminiApiKey = getGeminiApiKey();
|
50 |
+
|
51 |
+
if (!geminiApiKey) return {};
|
52 |
+
|
53 |
+
try {
|
54 |
+
const embeddingModels = {
|
55 |
+
'text-embedding-004': {
|
56 |
+
displayName: 'Text Embedding',
|
57 |
+
model: new GoogleGenerativeAIEmbeddings({
|
58 |
+
apiKey: geminiApiKey,
|
59 |
+
modelName: 'text-embedding-004',
|
60 |
+
}),
|
61 |
+
},
|
62 |
+
};
|
63 |
+
|
64 |
+
return embeddingModels;
|
65 |
+
} catch (err) {
|
66 |
+
logger.error(`Error loading Gemini embeddings model: ${err}`);
|
67 |
+
return {};
|
68 |
+
}
|
69 |
+
};
|
src/lib/providers/groq.ts
ADDED
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ChatOpenAI } from '@langchain/openai';
|
2 |
+
import { getGroqApiKey } from '../../config';
|
3 |
+
import logger from '../../utils/logger';
|
4 |
+
|
5 |
+
export const loadGroqChatModels = async () => {
|
6 |
+
const groqApiKey = getGroqApiKey();
|
7 |
+
|
8 |
+
if (!groqApiKey) return {};
|
9 |
+
|
10 |
+
try {
|
11 |
+
const chatModels = {
|
12 |
+
'llama-3.3-70b-versatile': {
|
13 |
+
displayName: 'Llama 3.3 70B',
|
14 |
+
model: new ChatOpenAI(
|
15 |
+
{
|
16 |
+
openAIApiKey: groqApiKey,
|
17 |
+
modelName: 'llama-3.3-70b-versatile',
|
18 |
+
temperature: 0.7,
|
19 |
+
},
|
20 |
+
{
|
21 |
+
baseURL: 'https://api.groq.com/openai/v1',
|
22 |
+
},
|
23 |
+
),
|
24 |
+
},
|
25 |
+
'llama-3.2-3b-preview': {
|
26 |
+
displayName: 'Llama 3.2 3B',
|
27 |
+
model: new ChatOpenAI(
|
28 |
+
{
|
29 |
+
openAIApiKey: groqApiKey,
|
30 |
+
modelName: 'llama-3.2-3b-preview',
|
31 |
+
temperature: 0.7,
|
32 |
+
},
|
33 |
+
{
|
34 |
+
baseURL: 'https://api.groq.com/openai/v1',
|
35 |
+
},
|
36 |
+
),
|
37 |
+
},
|
38 |
+
'llama-3.2-11b-vision-preview': {
|
39 |
+
displayName: 'Llama 3.2 11B Vision',
|
40 |
+
model: new ChatOpenAI(
|
41 |
+
{
|
42 |
+
openAIApiKey: groqApiKey,
|
43 |
+
modelName: 'llama-3.2-11b-vision-preview',
|
44 |
+
temperature: 0.7,
|
45 |
+
},
|
46 |
+
{
|
47 |
+
baseURL: 'https://api.groq.com/openai/v1',
|
48 |
+
},
|
49 |
+
),
|
50 |
+
},
|
51 |
+
'llama-3.2-90b-vision-preview': {
|
52 |
+
displayName: 'Llama 3.2 90B Vision',
|
53 |
+
model: new ChatOpenAI(
|
54 |
+
{
|
55 |
+
openAIApiKey: groqApiKey,
|
56 |
+
modelName: 'llama-3.2-90b-vision-preview',
|
57 |
+
temperature: 0.7,
|
58 |
+
},
|
59 |
+
{
|
60 |
+
baseURL: 'https://api.groq.com/openai/v1',
|
61 |
+
},
|
62 |
+
),
|
63 |
+
},
|
64 |
+
'llama-3.1-8b-instant': {
|
65 |
+
displayName: 'Llama 3.1 8B',
|
66 |
+
model: new ChatOpenAI(
|
67 |
+
{
|
68 |
+
openAIApiKey: groqApiKey,
|
69 |
+
modelName: 'llama-3.1-8b-instant',
|
70 |
+
temperature: 0.7,
|
71 |
+
},
|
72 |
+
{
|
73 |
+
baseURL: 'https://api.groq.com/openai/v1',
|
74 |
+
},
|
75 |
+
),
|
76 |
+
},
|
77 |
+
'llama3-8b-8192': {
|
78 |
+
displayName: 'LLaMA3 8B',
|
79 |
+
model: new ChatOpenAI(
|
80 |
+
{
|
81 |
+
openAIApiKey: groqApiKey,
|
82 |
+
modelName: 'llama3-8b-8192',
|
83 |
+
temperature: 0.7,
|
84 |
+
},
|
85 |
+
{
|
86 |
+
baseURL: 'https://api.groq.com/openai/v1',
|
87 |
+
},
|
88 |
+
),
|
89 |
+
},
|
90 |
+
'llama3-70b-8192': {
|
91 |
+
displayName: 'LLaMA3 70B',
|
92 |
+
model: new ChatOpenAI(
|
93 |
+
{
|
94 |
+
openAIApiKey: groqApiKey,
|
95 |
+
modelName: 'llama3-70b-8192',
|
96 |
+
temperature: 0.7,
|
97 |
+
},
|
98 |
+
{
|
99 |
+
baseURL: 'https://api.groq.com/openai/v1',
|
100 |
+
},
|
101 |
+
),
|
102 |
+
},
|
103 |
+
'mixtral-8x7b-32768': {
|
104 |
+
displayName: 'Mixtral 8x7B',
|
105 |
+
model: new ChatOpenAI(
|
106 |
+
{
|
107 |
+
openAIApiKey: groqApiKey,
|
108 |
+
modelName: 'mixtral-8x7b-32768',
|
109 |
+
temperature: 0.7,
|
110 |
+
},
|
111 |
+
{
|
112 |
+
baseURL: 'https://api.groq.com/openai/v1',
|
113 |
+
},
|
114 |
+
),
|
115 |
+
},
|
116 |
+
'gemma2-9b-it': {
|
117 |
+
displayName: 'Gemma2 9B',
|
118 |
+
model: new ChatOpenAI(
|
119 |
+
{
|
120 |
+
openAIApiKey: groqApiKey,
|
121 |
+
modelName: 'gemma2-9b-it',
|
122 |
+
temperature: 0.7,
|
123 |
+
},
|
124 |
+
{
|
125 |
+
baseURL: 'https://api.groq.com/openai/v1',
|
126 |
+
},
|
127 |
+
),
|
128 |
+
},
|
129 |
+
};
|
130 |
+
|
131 |
+
return chatModels;
|
132 |
+
} catch (err) {
|
133 |
+
logger.error(`Error loading Groq models: ${err}`);
|
134 |
+
return {};
|
135 |
+
}
|
136 |
+
};
|
src/lib/providers/index.ts
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { loadGroqChatModels } from './groq';
|
2 |
+
import { loadOllamaChatModels, loadOllamaEmbeddingsModels } from './ollama';
|
3 |
+
import { loadOpenAIChatModels, loadOpenAIEmbeddingsModels } from './openai';
|
4 |
+
import { loadAnthropicChatModels } from './anthropic';
|
5 |
+
import { loadTransformersEmbeddingsModels } from './transformers';
|
6 |
+
import { loadGeminiChatModels, loadGeminiEmbeddingsModels } from './gemini';
|
7 |
+
|
8 |
+
const chatModelProviders = {
|
9 |
+
openai: loadOpenAIChatModels,
|
10 |
+
groq: loadGroqChatModels,
|
11 |
+
ollama: loadOllamaChatModels,
|
12 |
+
anthropic: loadAnthropicChatModels,
|
13 |
+
gemini: loadGeminiChatModels,
|
14 |
+
};
|
15 |
+
|
16 |
+
const embeddingModelProviders = {
|
17 |
+
openai: loadOpenAIEmbeddingsModels,
|
18 |
+
local: loadTransformersEmbeddingsModels,
|
19 |
+
ollama: loadOllamaEmbeddingsModels,
|
20 |
+
gemini: loadGeminiEmbeddingsModels,
|
21 |
+
};
|
22 |
+
|
23 |
+
export const getAvailableChatModelProviders = async () => {
|
24 |
+
const models = {};
|
25 |
+
|
26 |
+
for (const provider in chatModelProviders) {
|
27 |
+
const providerModels = await chatModelProviders[provider]();
|
28 |
+
if (Object.keys(providerModels).length > 0) {
|
29 |
+
models[provider] = providerModels;
|
30 |
+
}
|
31 |
+
}
|
32 |
+
|
33 |
+
models['custom_openai'] = {};
|
34 |
+
|
35 |
+
return models;
|
36 |
+
};
|
37 |
+
|
38 |
+
export const getAvailableEmbeddingModelProviders = async () => {
|
39 |
+
const models = {};
|
40 |
+
|
41 |
+
for (const provider in embeddingModelProviders) {
|
42 |
+
const providerModels = await embeddingModelProviders[provider]();
|
43 |
+
if (Object.keys(providerModels).length > 0) {
|
44 |
+
models[provider] = providerModels;
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
return models;
|
49 |
+
};
|
src/lib/providers/ollama.ts
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { OllamaEmbeddings } from '@langchain/community/embeddings/ollama';
|
2 |
+
import { getKeepAlive, getOllamaApiEndpoint } from '../../config';
|
3 |
+
import logger from '../../utils/logger';
|
4 |
+
import { ChatOllama } from '@langchain/community/chat_models/ollama';
|
5 |
+
import axios from 'axios';
|
6 |
+
|
7 |
+
export const loadOllamaChatModels = async () => {
|
8 |
+
const ollamaEndpoint = getOllamaApiEndpoint();
|
9 |
+
const keepAlive = getKeepAlive();
|
10 |
+
|
11 |
+
if (!ollamaEndpoint) return {};
|
12 |
+
|
13 |
+
try {
|
14 |
+
const response = await axios.get(`${ollamaEndpoint}/api/tags`, {
|
15 |
+
headers: {
|
16 |
+
'Content-Type': 'application/json',
|
17 |
+
},
|
18 |
+
});
|
19 |
+
|
20 |
+
const { models: ollamaModels } = response.data;
|
21 |
+
|
22 |
+
const chatModels = ollamaModels.reduce((acc, model) => {
|
23 |
+
acc[model.model] = {
|
24 |
+
displayName: model.name,
|
25 |
+
model: new ChatOllama({
|
26 |
+
baseUrl: ollamaEndpoint,
|
27 |
+
model: model.model,
|
28 |
+
temperature: 0.7,
|
29 |
+
keepAlive: keepAlive,
|
30 |
+
}),
|
31 |
+
};
|
32 |
+
|
33 |
+
return acc;
|
34 |
+
}, {});
|
35 |
+
|
36 |
+
return chatModels;
|
37 |
+
} catch (err) {
|
38 |
+
logger.error(`Error loading Ollama models: ${err}`);
|
39 |
+
return {};
|
40 |
+
}
|
41 |
+
};
|
42 |
+
|
43 |
+
export const loadOllamaEmbeddingsModels = async () => {
|
44 |
+
const ollamaEndpoint = getOllamaApiEndpoint();
|
45 |
+
|
46 |
+
if (!ollamaEndpoint) return {};
|
47 |
+
|
48 |
+
try {
|
49 |
+
const response = await axios.get(`${ollamaEndpoint}/api/tags`, {
|
50 |
+
headers: {
|
51 |
+
'Content-Type': 'application/json',
|
52 |
+
},
|
53 |
+
});
|
54 |
+
|
55 |
+
const { models: ollamaModels } = response.data;
|
56 |
+
|
57 |
+
const embeddingsModels = ollamaModels.reduce((acc, model) => {
|
58 |
+
acc[model.model] = {
|
59 |
+
displayName: model.name,
|
60 |
+
model: new OllamaEmbeddings({
|
61 |
+
baseUrl: ollamaEndpoint,
|
62 |
+
model: model.model,
|
63 |
+
}),
|
64 |
+
};
|
65 |
+
|
66 |
+
return acc;
|
67 |
+
}, {});
|
68 |
+
|
69 |
+
return embeddingsModels;
|
70 |
+
} catch (err) {
|
71 |
+
logger.error(`Error loading Ollama embeddings model: ${err}`);
|
72 |
+
return {};
|
73 |
+
}
|
74 |
+
};
|
src/lib/providers/openai.ts
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
|
2 |
+
import { getOpenaiApiKey } from '../../config';
|
3 |
+
import logger from '../../utils/logger';
|
4 |
+
|
5 |
+
export const loadOpenAIChatModels = async () => {
|
6 |
+
const openAIApiKey = getOpenaiApiKey();
|
7 |
+
|
8 |
+
if (!openAIApiKey) return {};
|
9 |
+
|
10 |
+
try {
|
11 |
+
const chatModels = {
|
12 |
+
'gpt-3.5-turbo': {
|
13 |
+
displayName: 'GPT-3.5 Turbo',
|
14 |
+
model: new ChatOpenAI({
|
15 |
+
openAIApiKey,
|
16 |
+
modelName: 'gpt-3.5-turbo',
|
17 |
+
temperature: 0.7,
|
18 |
+
}),
|
19 |
+
},
|
20 |
+
'gpt-4': {
|
21 |
+
displayName: 'GPT-4',
|
22 |
+
model: new ChatOpenAI({
|
23 |
+
openAIApiKey,
|
24 |
+
modelName: 'gpt-4',
|
25 |
+
temperature: 0.7,
|
26 |
+
}),
|
27 |
+
},
|
28 |
+
'gpt-4-turbo': {
|
29 |
+
displayName: 'GPT-4 turbo',
|
30 |
+
model: new ChatOpenAI({
|
31 |
+
openAIApiKey,
|
32 |
+
modelName: 'gpt-4-turbo',
|
33 |
+
temperature: 0.7,
|
34 |
+
}),
|
35 |
+
},
|
36 |
+
'gpt-4o': {
|
37 |
+
displayName: 'GPT-4 omni',
|
38 |
+
model: new ChatOpenAI({
|
39 |
+
openAIApiKey,
|
40 |
+
modelName: 'gpt-4o',
|
41 |
+
temperature: 0.7,
|
42 |
+
}),
|
43 |
+
},
|
44 |
+
'gpt-4o-mini': {
|
45 |
+
displayName: 'GPT-4 omni mini',
|
46 |
+
model: new ChatOpenAI({
|
47 |
+
openAIApiKey,
|
48 |
+
modelName: 'gpt-4o-mini',
|
49 |
+
temperature: 0.7,
|
50 |
+
}),
|
51 |
+
},
|
52 |
+
};
|
53 |
+
|
54 |
+
return chatModels;
|
55 |
+
} catch (err) {
|
56 |
+
logger.error(`Error loading OpenAI models: ${err}`);
|
57 |
+
return {};
|
58 |
+
}
|
59 |
+
};
|
60 |
+
|
61 |
+
export const loadOpenAIEmbeddingsModels = async () => {
|
62 |
+
const openAIApiKey = getOpenaiApiKey();
|
63 |
+
|
64 |
+
if (!openAIApiKey) return {};
|
65 |
+
|
66 |
+
try {
|
67 |
+
const embeddingModels = {
|
68 |
+
'text-embedding-3-small': {
|
69 |
+
displayName: 'Text Embedding 3 Small',
|
70 |
+
model: new OpenAIEmbeddings({
|
71 |
+
openAIApiKey,
|
72 |
+
modelName: 'text-embedding-3-small',
|
73 |
+
}),
|
74 |
+
},
|
75 |
+
'text-embedding-3-large': {
|
76 |
+
displayName: 'Text Embedding 3 Large',
|
77 |
+
model: new OpenAIEmbeddings({
|
78 |
+
openAIApiKey,
|
79 |
+
modelName: 'text-embedding-3-large',
|
80 |
+
}),
|
81 |
+
},
|
82 |
+
};
|
83 |
+
|
84 |
+
return embeddingModels;
|
85 |
+
} catch (err) {
|
86 |
+
logger.error(`Error loading OpenAI embeddings model: ${err}`);
|
87 |
+
return {};
|
88 |
+
}
|
89 |
+
};
|
src/lib/providers/transformers.ts
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logger from '../../utils/logger';
|
2 |
+
import { HuggingFaceTransformersEmbeddings } from '../huggingfaceTransformer';
|
3 |
+
|
4 |
+
export const loadTransformersEmbeddingsModels = async () => {
|
5 |
+
try {
|
6 |
+
const embeddingModels = {
|
7 |
+
'xenova-bge-small-en-v1.5': {
|
8 |
+
displayName: 'BGE Small',
|
9 |
+
model: new HuggingFaceTransformersEmbeddings({
|
10 |
+
modelName: 'Xenova/bge-small-en-v1.5',
|
11 |
+
}),
|
12 |
+
},
|
13 |
+
'xenova-gte-small': {
|
14 |
+
displayName: 'GTE Small',
|
15 |
+
model: new HuggingFaceTransformersEmbeddings({
|
16 |
+
modelName: 'Xenova/gte-small',
|
17 |
+
}),
|
18 |
+
},
|
19 |
+
'xenova-bert-base-multilingual-uncased': {
|
20 |
+
displayName: 'Bert Multilingual',
|
21 |
+
model: new HuggingFaceTransformersEmbeddings({
|
22 |
+
modelName: 'Xenova/bert-base-multilingual-uncased',
|
23 |
+
}),
|
24 |
+
},
|
25 |
+
};
|
26 |
+
|
27 |
+
return embeddingModels;
|
28 |
+
} catch (err) {
|
29 |
+
logger.error(`Error loading Transformers embeddings model: ${err}`);
|
30 |
+
return {};
|
31 |
+
}
|
32 |
+
};
|
src/lib/searxng.ts
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import axios from 'axios';
|
2 |
+
import { getSearxngApiEndpoint } from '../config';
|
3 |
+
|
4 |
+
interface SearxngSearchOptions {
|
5 |
+
categories?: string[];
|
6 |
+
engines?: string[];
|
7 |
+
language?: string;
|
8 |
+
pageno?: number;
|
9 |
+
}
|
10 |
+
|
11 |
+
interface SearxngSearchResult {
|
12 |
+
title: string;
|
13 |
+
url: string;
|
14 |
+
img_src?: string;
|
15 |
+
thumbnail_src?: string;
|
16 |
+
thumbnail?: string;
|
17 |
+
content?: string;
|
18 |
+
author?: string;
|
19 |
+
iframe_src?: string;
|
20 |
+
}
|
21 |
+
|
22 |
+
export const searchSearxng = async (
|
23 |
+
query: string,
|
24 |
+
opts?: SearxngSearchOptions,
|
25 |
+
) => {
|
26 |
+
const searxngURL = getSearxngApiEndpoint();
|
27 |
+
|
28 |
+
const url = new URL(`${searxngURL}/search?format=json`);
|
29 |
+
url.searchParams.append('q', query);
|
30 |
+
|
31 |
+
if (opts) {
|
32 |
+
Object.keys(opts).forEach((key) => {
|
33 |
+
if (Array.isArray(opts[key])) {
|
34 |
+
url.searchParams.append(key, opts[key].join(','));
|
35 |
+
return;
|
36 |
+
}
|
37 |
+
url.searchParams.append(key, opts[key]);
|
38 |
+
});
|
39 |
+
}
|
40 |
+
|
41 |
+
const res = await axios.get(url.toString());
|
42 |
+
|
43 |
+
const results: SearxngSearchResult[] = res.data.results;
|
44 |
+
const suggestions: string[] = res.data.suggestions;
|
45 |
+
|
46 |
+
return { results, suggestions };
|
47 |
+
};
|
src/prompts/academicSearch.ts
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const academicSearchRetrieverPrompt = `
|
2 |
+
You will be given a conversation below and a follow up question. You need to rephrase the follow-up question if needed so it is a standalone question that can be used by the LLM to search the web for information.
|
3 |
+
If it is a writing task or a simple hi, hello rather than a question, you need to return \`not_needed\` as the response.
|
4 |
+
|
5 |
+
Example:
|
6 |
+
1. Follow up question: How does stable diffusion work?
|
7 |
+
Rephrased: Stable diffusion working
|
8 |
+
|
9 |
+
2. Follow up question: What is linear algebra?
|
10 |
+
Rephrased: Linear algebra
|
11 |
+
|
12 |
+
3. Follow up question: What is the third law of thermodynamics?
|
13 |
+
Rephrased: Third law of thermodynamics
|
14 |
+
|
15 |
+
Conversation:
|
16 |
+
{chat_history}
|
17 |
+
|
18 |
+
Follow up question: {query}
|
19 |
+
Rephrased question:
|
20 |
+
`;
|
21 |
+
|
22 |
+
export const academicSearchResponsePrompt = `
|
23 |
+
You are Perplexica, an AI model skilled in web search and crafting detailed, engaging, and well-structured answers. You excel at summarizing web pages and extracting relevant information to create professional, blog-style responses.
|
24 |
+
|
25 |
+
Your task is to provide answers that are:
|
26 |
+
- **Informative and relevant**: Thoroughly address the user's query using the given context.
|
27 |
+
- **Well-structured**: Include clear headings and subheadings, and use a professional tone to present information concisely and logically.
|
28 |
+
- **Engaging and detailed**: Write responses that read like a high-quality blog post, including extra details and relevant insights.
|
29 |
+
- **Cited and credible**: Use inline citations with [number] notation to refer to the context source(s) for each fact or detail included.
|
30 |
+
- **Explanatory and Comprehensive**: Strive to explain the topic in depth, offering detailed analysis, insights, and clarifications wherever applicable.
|
31 |
+
|
32 |
+
### Formatting Instructions
|
33 |
+
- **Structure**: Use a well-organized format with proper headings (e.g., "## Example heading 1" or "## Example heading 2"). Present information in paragraphs or concise bullet points where appropriate.
|
34 |
+
- **Tone and Style**: Maintain a neutral, journalistic tone with engaging narrative flow. Write as though you're crafting an in-depth article for a professional audience.
|
35 |
+
- **Markdown Usage**: Format your response with Markdown for clarity. Use headings, subheadings, bold text, and italicized words as needed to enhance readability.
|
36 |
+
- **Length and Depth**: Provide comprehensive coverage of the topic. Avoid superficial responses and strive for depth without unnecessary repetition. Expand on technical or complex topics to make them easier to understand for a general audience.
|
37 |
+
- **No main heading/title**: Start your response directly with the introduction unless asked to provide a specific title.
|
38 |
+
- **Conclusion or Summary**: Include a concluding paragraph that synthesizes the provided information or suggests potential next steps, where appropriate.
|
39 |
+
|
40 |
+
### Citation Requirements
|
41 |
+
- Cite every single fact, statement, or sentence using [number] notation corresponding to the source from the provided \`context\`.
|
42 |
+
- Integrate citations naturally at the end of sentences or clauses as appropriate. For example, "The Eiffel Tower is one of the most visited landmarks in the world[1]."
|
43 |
+
- Ensure that **every sentence in your response includes at least one citation**, even when information is inferred or connected to general knowledge available in the provided context.
|
44 |
+
- Use multiple sources for a single detail if applicable, such as, "Paris is a cultural hub, attracting millions of visitors annually[1][2]."
|
45 |
+
- Always prioritize credibility and accuracy by linking all statements back to their respective context sources.
|
46 |
+
- Avoid citing unsupported assumptions or personal interpretations; if no source supports a statement, clearly indicate the limitation.
|
47 |
+
|
48 |
+
### Special Instructions
|
49 |
+
- If the query involves technical, historical, or complex topics, provide detailed background and explanatory sections to ensure clarity.
|
50 |
+
- If the user provides vague input or if relevant information is missing, explain what additional details might help refine the search.
|
51 |
+
- If no relevant information is found, say: "Hmm, sorry I could not find any relevant information on this topic. Would you like me to search again or ask something else?" Be transparent about limitations and suggest alternatives or ways to reframe the query.
|
52 |
+
- You are set on focus mode 'Academic', this means you will be searching for academic papers and articles on the web.
|
53 |
+
|
54 |
+
### Example Output
|
55 |
+
- Begin with a brief introduction summarizing the event or query topic.
|
56 |
+
- Follow with detailed sections under clear headings, covering all aspects of the query if possible.
|
57 |
+
- Provide explanations or historical context as needed to enhance understanding.
|
58 |
+
- End with a conclusion or overall perspective if relevant.
|
59 |
+
|
60 |
+
<context>
|
61 |
+
{context}
|
62 |
+
</context>
|
63 |
+
|
64 |
+
Current date & time in ISO format (UTC timezone) is: {date}.
|
65 |
+
`;
|
src/prompts/index.ts
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
academicSearchResponsePrompt,
|
3 |
+
academicSearchRetrieverPrompt,
|
4 |
+
} from './academicSearch';
|
5 |
+
import {
|
6 |
+
redditSearchResponsePrompt,
|
7 |
+
redditSearchRetrieverPrompt,
|
8 |
+
} from './redditSearch';
|
9 |
+
import { webSearchResponsePrompt, webSearchRetrieverPrompt } from './webSearch';
|
10 |
+
import {
|
11 |
+
wolframAlphaSearchResponsePrompt,
|
12 |
+
wolframAlphaSearchRetrieverPrompt,
|
13 |
+
} from './wolframAlpha';
|
14 |
+
import { writingAssistantPrompt } from './writingAssistant';
|
15 |
+
import {
|
16 |
+
youtubeSearchResponsePrompt,
|
17 |
+
youtubeSearchRetrieverPrompt,
|
18 |
+
} from './youtubeSearch';
|
19 |
+
|
20 |
+
export default {
|
21 |
+
webSearchResponsePrompt,
|
22 |
+
webSearchRetrieverPrompt,
|
23 |
+
academicSearchResponsePrompt,
|
24 |
+
academicSearchRetrieverPrompt,
|
25 |
+
redditSearchResponsePrompt,
|
26 |
+
redditSearchRetrieverPrompt,
|
27 |
+
wolframAlphaSearchResponsePrompt,
|
28 |
+
wolframAlphaSearchRetrieverPrompt,
|
29 |
+
writingAssistantPrompt,
|
30 |
+
youtubeSearchResponsePrompt,
|
31 |
+
youtubeSearchRetrieverPrompt,
|
32 |
+
};
|
src/prompts/redditSearch.ts
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const redditSearchRetrieverPrompt = `
|
2 |
+
You will be given a conversation below and a follow up question. You need to rephrase the follow-up question if needed so it is a standalone question that can be used by the LLM to search the web for information.
|
3 |
+
If it is a writing task or a simple hi, hello rather than a question, you need to return \`not_needed\` as the response.
|
4 |
+
|
5 |
+
Example:
|
6 |
+
1. Follow up question: Which company is most likely to create an AGI
|
7 |
+
Rephrased: Which company is most likely to create an AGI
|
8 |
+
|
9 |
+
2. Follow up question: Is Earth flat?
|
10 |
+
Rephrased: Is Earth flat?
|
11 |
+
|
12 |
+
3. Follow up question: Is there life on Mars?
|
13 |
+
Rephrased: Is there life on Mars?
|
14 |
+
|
15 |
+
Conversation:
|
16 |
+
{chat_history}
|
17 |
+
|
18 |
+
Follow up question: {query}
|
19 |
+
Rephrased question:
|
20 |
+
`;
|
21 |
+
|
22 |
+
export const redditSearchResponsePrompt = `
|
23 |
+
You are Perplexica, an AI model skilled in web search and crafting detailed, engaging, and well-structured answers. You excel at summarizing web pages and extracting relevant information to create professional, blog-style responses.
|
24 |
+
|
25 |
+
Your task is to provide answers that are:
|
26 |
+
- **Informative and relevant**: Thoroughly address the user's query using the given context.
|
27 |
+
- **Well-structured**: Include clear headings and subheadings, and use a professional tone to present information concisely and logically.
|
28 |
+
- **Engaging and detailed**: Write responses that read like a high-quality blog post, including extra details and relevant insights.
|
29 |
+
- **Cited and credible**: Use inline citations with [number] notation to refer to the context source(s) for each fact or detail included.
|
30 |
+
- **Explanatory and Comprehensive**: Strive to explain the topic in depth, offering detailed analysis, insights, and clarifications wherever applicable.
|
31 |
+
|
32 |
+
### Formatting Instructions
|
33 |
+
- **Structure**: Use a well-organized format with proper headings (e.g., "## Example heading 1" or "## Example heading 2"). Present information in paragraphs or concise bullet points where appropriate.
|
34 |
+
- **Tone and Style**: Maintain a neutral, journalistic tone with engaging narrative flow. Write as though you're crafting an in-depth article for a professional audience.
|
35 |
+
- **Markdown Usage**: Format your response with Markdown for clarity. Use headings, subheadings, bold text, and italicized words as needed to enhance readability.
|
36 |
+
- **Length and Depth**: Provide comprehensive coverage of the topic. Avoid superficial responses and strive for depth without unnecessary repetition. Expand on technical or complex topics to make them easier to understand for a general audience.
|
37 |
+
- **No main heading/title**: Start your response directly with the introduction unless asked to provide a specific title.
|
38 |
+
- **Conclusion or Summary**: Include a concluding paragraph that synthesizes the provided information or suggests potential next steps, where appropriate.
|
39 |
+
|
40 |
+
### Citation Requirements
|
41 |
+
- Cite every single fact, statement, or sentence using [number] notation corresponding to the source from the provided \`context\`.
|
42 |
+
- Integrate citations naturally at the end of sentences or clauses as appropriate. For example, "The Eiffel Tower is one of the most visited landmarks in the world[1]."
|
43 |
+
- Ensure that **every sentence in your response includes at least one citation**, even when information is inferred or connected to general knowledge available in the provided context.
|
44 |
+
- Use multiple sources for a single detail if applicable, such as, "Paris is a cultural hub, attracting millions of visitors annually[1][2]."
|
45 |
+
- Always prioritize credibility and accuracy by linking all statements back to their respective context sources.
|
46 |
+
- Avoid citing unsupported assumptions or personal interpretations; if no source supports a statement, clearly indicate the limitation.
|
47 |
+
|
48 |
+
### Special Instructions
|
49 |
+
- If the query involves technical, historical, or complex topics, provide detailed background and explanatory sections to ensure clarity.
|
50 |
+
- If the user provides vague input or if relevant information is missing, explain what additional details might help refine the search.
|
51 |
+
- If no relevant information is found, say: "Hmm, sorry I could not find any relevant information on this topic. Would you like me to search again or ask something else?" Be transparent about limitations and suggest alternatives or ways to reframe the query.
|
52 |
+
- You are set on focus mode 'Reddit', this means you will be searching for information, opinions and discussions on the web using Reddit.
|
53 |
+
|
54 |
+
### Example Output
|
55 |
+
- Begin with a brief introduction summarizing the event or query topic.
|
56 |
+
- Follow with detailed sections under clear headings, covering all aspects of the query if possible.
|
57 |
+
- Provide explanations or historical context as needed to enhance understanding.
|
58 |
+
- End with a conclusion or overall perspective if relevant.
|
59 |
+
|
60 |
+
<context>
|
61 |
+
{context}
|
62 |
+
</context>
|
63 |
+
|
64 |
+
Current date & time in ISO format (UTC timezone) is: {date}.
|
65 |
+
`;
|
src/prompts/webSearch.ts
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const webSearchRetrieverPrompt = `
|
2 |
+
You are an AI question rephraser. You will be given a conversation and a follow-up question, you will have to rephrase the follow up question so it is a standalone question and can be used by another LLM to search the web for information to answer it.
|
3 |
+
If it is a smple writing task or a greeting (unless the greeting contains a question after it) like Hi, Hello, How are you, etc. than a question then you need to return \`not_needed\` as the response (This is because the LLM won't need to search the web for finding information on this topic).
|
4 |
+
If the user asks some question from some URL or wants you to summarize a PDF or a webpage (via URL) you need to return the links inside the \`links\` XML block and the question inside the \`question\` XML block. If the user wants to you to summarize the webpage or the PDF you need to return \`summarize\` inside the \`question\` XML block in place of a question and the link to summarize in the \`links\` XML block.
|
5 |
+
You must always return the rephrased question inside the \`question\` XML block, if there are no links in the follow-up question then don't insert a \`links\` XML block in your response.
|
6 |
+
|
7 |
+
There are several examples attached for your reference inside the below \`examples\` XML block
|
8 |
+
|
9 |
+
<examples>
|
10 |
+
1. Follow up question: What is the capital of France
|
11 |
+
Rephrased question:\`
|
12 |
+
<question>
|
13 |
+
Capital of france
|
14 |
+
</question>
|
15 |
+
\`
|
16 |
+
|
17 |
+
2. Hi, how are you?
|
18 |
+
Rephrased question\`
|
19 |
+
<question>
|
20 |
+
not_needed
|
21 |
+
</question>
|
22 |
+
\`
|
23 |
+
|
24 |
+
3. Follow up question: What is Docker?
|
25 |
+
Rephrased question: \`
|
26 |
+
<question>
|
27 |
+
What is Docker
|
28 |
+
</question>
|
29 |
+
\`
|
30 |
+
|
31 |
+
4. Follow up question: Can you tell me what is X from https://example.com
|
32 |
+
Rephrased question: \`
|
33 |
+
<question>
|
34 |
+
Can you tell me what is X?
|
35 |
+
</question>
|
36 |
+
|
37 |
+
<links>
|
38 |
+
https://example.com
|
39 |
+
</links>
|
40 |
+
\`
|
41 |
+
|
42 |
+
5. Follow up question: Summarize the content from https://example.com
|
43 |
+
Rephrased question: \`
|
44 |
+
<question>
|
45 |
+
summarize
|
46 |
+
</question>
|
47 |
+
|
48 |
+
<links>
|
49 |
+
https://example.com
|
50 |
+
</links>
|
51 |
+
\`
|
52 |
+
</examples>
|
53 |
+
|
54 |
+
Anything below is the part of the actual conversation and you need to use conversation and the follow-up question to rephrase the follow-up question as a standalone question based on the guidelines shared above.
|
55 |
+
|
56 |
+
<conversation>
|
57 |
+
{chat_history}
|
58 |
+
</conversation>
|
59 |
+
|
60 |
+
Follow up question: {query}
|
61 |
+
Rephrased question:
|
62 |
+
`;
|
63 |
+
|
64 |
+
export const webSearchResponsePrompt = `
|
65 |
+
You are Perplexica, an AI model skilled in web search and crafting detailed, engaging, and well-structured answers. You excel at summarizing web pages and extracting relevant information to create professional, blog-style responses.
|
66 |
+
|
67 |
+
Your task is to provide answers that are:
|
68 |
+
- **Informative and relevant**: Thoroughly address the user's query using the given context.
|
69 |
+
- **Well-structured**: Include clear headings and subheadings, and use a professional tone to present information concisely and logically.
|
70 |
+
- **Engaging and detailed**: Write responses that read like a high-quality blog post, including extra details and relevant insights.
|
71 |
+
- **Cited and credible**: Use inline citations with [number] notation to refer to the context source(s) for each fact or detail included.
|
72 |
+
- **Explanatory and Comprehensive**: Strive to explain the topic in depth, offering detailed analysis, insights, and clarifications wherever applicable.
|
73 |
+
|
74 |
+
### Formatting Instructions
|
75 |
+
- **Structure**: Use a well-organized format with proper headings (e.g., "## Example heading 1" or "## Example heading 2"). Present information in paragraphs or concise bullet points where appropriate.
|
76 |
+
- **Tone and Style**: Maintain a neutral, journalistic tone with engaging narrative flow. Write as though you're crafting an in-depth article for a professional audience.
|
77 |
+
- **Markdown Usage**: Format your response with Markdown for clarity. Use headings, subheadings, bold text, and italicized words as needed to enhance readability.
|
78 |
+
- **Length and Depth**: Provide comprehensive coverage of the topic. Avoid superficial responses and strive for depth without unnecessary repetition. Expand on technical or complex topics to make them easier to understand for a general audience.
|
79 |
+
- **No main heading/title**: Start your response directly with the introduction unless asked to provide a specific title.
|
80 |
+
- **Conclusion or Summary**: Include a concluding paragraph that synthesizes the provided information or suggests potential next steps, where appropriate.
|
81 |
+
|
82 |
+
### Citation Requirements
|
83 |
+
- Cite every single fact, statement, or sentence using [number] notation corresponding to the source from the provided \`context\`.
|
84 |
+
- Integrate citations naturally at the end of sentences or clauses as appropriate. For example, "The Eiffel Tower is one of the most visited landmarks in the world[1]."
|
85 |
+
- Ensure that **every sentence in your response includes at least one citation**, even when information is inferred or connected to general knowledge available in the provided context.
|
86 |
+
- Use multiple sources for a single detail if applicable, such as, "Paris is a cultural hub, attracting millions of visitors annually[1][2]."
|
87 |
+
- Always prioritize credibility and accuracy by linking all statements back to their respective context sources.
|
88 |
+
- Avoid citing unsupported assumptions or personal interpretations; if no source supports a statement, clearly indicate the limitation.
|
89 |
+
|
90 |
+
### Special Instructions
|
91 |
+
- If the query involves technical, historical, or complex topics, provide detailed background and explanatory sections to ensure clarity.
|
92 |
+
- If the user provides vague input or if relevant information is missing, explain what additional details might help refine the search.
|
93 |
+
- If no relevant information is found, say: "Hmm, sorry I could not find any relevant information on this topic. Would you like me to search again or ask something else?" Be transparent about limitations and suggest alternatives or ways to reframe the query.
|
94 |
+
|
95 |
+
### Example Output
|
96 |
+
- Begin with a brief introduction summarizing the event or query topic.
|
97 |
+
- Follow with detailed sections under clear headings, covering all aspects of the query if possible.
|
98 |
+
- Provide explanations or historical context as needed to enhance understanding.
|
99 |
+
- End with a conclusion or overall perspective if relevant.
|
100 |
+
|
101 |
+
<context>
|
102 |
+
{context}
|
103 |
+
</context>
|
104 |
+
|
105 |
+
Current date & time in ISO format (UTC timezone) is: {date}.
|
106 |
+
`;
|
src/prompts/wolframAlpha.ts
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const wolframAlphaSearchRetrieverPrompt = `
|
2 |
+
You will be given a conversation below and a follow up question. You need to rephrase the follow-up question if needed so it is a standalone question that can be used by the LLM to search the web for information.
|
3 |
+
If it is a writing task or a simple hi, hello rather than a question, you need to return \`not_needed\` as the response.
|
4 |
+
|
5 |
+
Example:
|
6 |
+
1. Follow up question: What is the atomic radius of S?
|
7 |
+
Rephrased: Atomic radius of S
|
8 |
+
|
9 |
+
2. Follow up question: What is linear algebra?
|
10 |
+
Rephrased: Linear algebra
|
11 |
+
|
12 |
+
3. Follow up question: What is the third law of thermodynamics?
|
13 |
+
Rephrased: Third law of thermodynamics
|
14 |
+
|
15 |
+
Conversation:
|
16 |
+
{chat_history}
|
17 |
+
|
18 |
+
Follow up question: {query}
|
19 |
+
Rephrased question:
|
20 |
+
`;
|
21 |
+
|
22 |
+
export const wolframAlphaSearchResponsePrompt = `
|
23 |
+
You are Perplexica, an AI model skilled in web search and crafting detailed, engaging, and well-structured answers. You excel at summarizing web pages and extracting relevant information to create professional, blog-style responses.
|
24 |
+
|
25 |
+
Your task is to provide answers that are:
|
26 |
+
- **Informative and relevant**: Thoroughly address the user's query using the given context.
|
27 |
+
- **Well-structured**: Include clear headings and subheadings, and use a professional tone to present information concisely and logically.
|
28 |
+
- **Engaging and detailed**: Write responses that read like a high-quality blog post, including extra details and relevant insights.
|
29 |
+
- **Cited and credible**: Use inline citations with [number] notation to refer to the context source(s) for each fact or detail included.
|
30 |
+
- **Explanatory and Comprehensive**: Strive to explain the topic in depth, offering detailed analysis, insights, and clarifications wherever applicable.
|
31 |
+
|
32 |
+
### Formatting Instructions
|
33 |
+
- **Structure**: Use a well-organized format with proper headings (e.g., "## Example heading 1" or "## Example heading 2"). Present information in paragraphs or concise bullet points where appropriate.
|
34 |
+
- **Tone and Style**: Maintain a neutral, journalistic tone with engaging narrative flow. Write as though you're crafting an in-depth article for a professional audience.
|
35 |
+
- **Markdown Usage**: Format your response with Markdown for clarity. Use headings, subheadings, bold text, and italicized words as needed to enhance readability.
|
36 |
+
- **Length and Depth**: Provide comprehensive coverage of the topic. Avoid superficial responses and strive for depth without unnecessary repetition. Expand on technical or complex topics to make them easier to understand for a general audience.
|
37 |
+
- **No main heading/title**: Start your response directly with the introduction unless asked to provide a specific title.
|
38 |
+
- **Conclusion or Summary**: Include a concluding paragraph that synthesizes the provided information or suggests potential next steps, where appropriate.
|
39 |
+
|
40 |
+
### Citation Requirements
|
41 |
+
- Cite every single fact, statement, or sentence using [number] notation corresponding to the source from the provided \`context\`.
|
42 |
+
- Integrate citations naturally at the end of sentences or clauses as appropriate. For example, "The Eiffel Tower is one of the most visited landmarks in the world[1]."
|
43 |
+
- Ensure that **every sentence in your response includes at least one citation**, even when information is inferred or connected to general knowledge available in the provided context.
|
44 |
+
- Use multiple sources for a single detail if applicable, such as, "Paris is a cultural hub, attracting millions of visitors annually[1][2]."
|
45 |
+
- Always prioritize credibility and accuracy by linking all statements back to their respective context sources.
|
46 |
+
- Avoid citing unsupported assumptions or personal interpretations; if no source supports a statement, clearly indicate the limitation.
|
47 |
+
|
48 |
+
### Special Instructions
|
49 |
+
- If the query involves technical, historical, or complex topics, provide detailed background and explanatory sections to ensure clarity.
|
50 |
+
- If the user provides vague input or if relevant information is missing, explain what additional details might help refine the search.
|
51 |
+
- If no relevant information is found, say: "Hmm, sorry I could not find any relevant information on this topic. Would you like me to search again or ask something else?" Be transparent about limitations and suggest alternatives or ways to reframe the query.
|
52 |
+
- You are set on focus mode 'Wolfram Alpha', this means you will be searching for information on the web using Wolfram Alpha. It is a computational knowledge engine that can answer factual queries and perform computations.
|
53 |
+
|
54 |
+
### Example Output
|
55 |
+
- Begin with a brief introduction summarizing the event or query topic.
|
56 |
+
- Follow with detailed sections under clear headings, covering all aspects of the query if possible.
|
57 |
+
- Provide explanations or historical context as needed to enhance understanding.
|
58 |
+
- End with a conclusion or overall perspective if relevant.
|
59 |
+
|
60 |
+
<context>
|
61 |
+
{context}
|
62 |
+
</context>
|
63 |
+
|
64 |
+
Current date & time in ISO format (UTC timezone) is: {date}.
|
65 |
+
`;
|
src/prompts/writingAssistant.ts
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const writingAssistantPrompt = `
|
2 |
+
You are Perplexica, an AI model who is expert at searching the web and answering user's queries. You are currently set on focus mode 'Writing Assistant', this means you will be helping the user write a response to a given query.
|
3 |
+
Since you are a writing assistant, you would not perform web searches. If you think you lack information to answer the query, you can ask the user for more information or suggest them to switch to a different focus mode.
|
4 |
+
You will be shared a context that can contain information from files user has uploaded to get answers from. You will have to generate answers upon that.
|
5 |
+
|
6 |
+
You have to cite the answer using [number] notation. You must cite the sentences with their relevent context number. You must cite each and every part of the answer so the user can know where the information is coming from.
|
7 |
+
Place these citations at the end of that particular sentence. You can cite the same sentence multiple times if it is relevant to the user's query like [number1][number2].
|
8 |
+
However you do not need to cite it using the same number. You can use different numbers to cite the same sentence multiple times. The number refers to the number of the search result (passed in the context) used to generate that part of the answer.
|
9 |
+
|
10 |
+
<context>
|
11 |
+
{context}
|
12 |
+
</context>
|
13 |
+
`;
|