0Scottzilla0 commited on
Commit
a80ecb8
·
verified ·
1 Parent(s): 7045d1d

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .assets/perplexica-preview.gif +3 -0
  2. .assets/perplexica-screenshot.png +3 -0
  3. .dockerignore +1 -0
  4. .gitattributes +2 -0
  5. .github/ISSUE_TEMPLATE/bug_report.md +27 -0
  6. .github/ISSUE_TEMPLATE/custom.md +7 -0
  7. .github/ISSUE_TEMPLATE/feature_request.md +19 -0
  8. .github/workflows/docker-build.yaml +73 -0
  9. .gitignore +39 -0
  10. .prettierignore +41 -0
  11. .prettierrc.js +12 -0
  12. CONTRIBUTING.md +41 -0
  13. LICENSE +21 -0
  14. README.md +178 -10
  15. app.dockerfile +15 -0
  16. backend.dockerfile +17 -0
  17. data/.gitignore +2 -0
  18. docker-compose.yaml +54 -0
  19. docs/API/SEARCH.md +117 -0
  20. docs/architecture/README.md +11 -0
  21. docs/architecture/WORKING.md +19 -0
  22. docs/installation/NETWORKING.md +109 -0
  23. docs/installation/UPDATING.md +40 -0
  24. drizzle.config.ts +10 -0
  25. package.json +53 -0
  26. sample.config.toml +14 -0
  27. src/app.ts +38 -0
  28. src/chains/imageSearchAgent.ts +84 -0
  29. src/chains/suggestionGeneratorAgent.ts +55 -0
  30. src/chains/videoSearchAgent.ts +90 -0
  31. src/config.ts +79 -0
  32. src/db/index.ts +10 -0
  33. src/db/schema.ts +28 -0
  34. src/lib/huggingfaceTransformer.ts +82 -0
  35. src/lib/outputParsers/lineOutputParser.ts +48 -0
  36. src/lib/outputParsers/listLineOutputParser.ts +50 -0
  37. src/lib/providers/anthropic.ts +59 -0
  38. src/lib/providers/gemini.ts +69 -0
  39. src/lib/providers/groq.ts +136 -0
  40. src/lib/providers/index.ts +49 -0
  41. src/lib/providers/ollama.ts +74 -0
  42. src/lib/providers/openai.ts +89 -0
  43. src/lib/providers/transformers.ts +32 -0
  44. src/lib/searxng.ts +47 -0
  45. src/prompts/academicSearch.ts +65 -0
  46. src/prompts/index.ts +32 -0
  47. src/prompts/redditSearch.ts +65 -0
  48. src/prompts/webSearch.ts +106 -0
  49. src/prompts/wolframAlpha.ts +65 -0
  50. src/prompts/writingAssistant.ts +13 -0
.assets/perplexica-preview.gif ADDED

Git LFS Details

  • SHA256: 4b8d709c598521456e35b03951c19fdcd77a07881af6200b99c68224dbd5fbd9
  • Pointer size: 133 Bytes
  • Size of remote file: 16.4 MB
.assets/perplexica-screenshot.png ADDED

Git LFS Details

  • SHA256: c7277085b6068417af6d301199d7ba4acdb877dbed6396b9c38ba0e7f4cf6229
  • Pointer size: 131 Bytes
  • Size of remote file: 656 kB
.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
- title: Perplexican
3
- emoji: 👀
4
- colorFrom: pink
5
- colorTo: green
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚀 Perplexica - An AI-powered search engine 🔎 <!-- omit in toc -->
2
+
3
+ [![Discord](https://dcbadge.vercel.app/api/server/26aArMy8tT?style=flat&compact=true)](https://discord.gg/26aArMy8tT)
4
+
5
+
6
+ ![preview](.assets/perplexica-screenshot.png?)
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
+ ![video-preview](.assets/perplexica-preview.gif)
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
+ [![Deploy to RepoCloud](https://d16t0pc4846x52.cloudfront.net/deploylobe.svg)](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
+ `;