The Problem
Users who need on‑premise, citation‑backed answers must stitch together a web server, LLM providers, a search backend and a UI. Doing this manually requires wiring many moving parts, handling secret keys, and keeping the data flow private.
What This Does
Perplexica delivers a self‑hosted Next.js application that exposes a REST‑style API (src/app/api/**) and a React front‑end. The search flow lives in src/lib/agents/search/, with provider adapters in src/lib/models/providers/ (e.g., OpenAI, Anthropic, Ollama). Configuration and persistence are handled by src/lib/config/ and src/lib/db/.
Key files:
src/app/api/search/route.ts– HTTP handler that starts a search (start).src/lib/agents/search/index.ts–searchAsyncorchestrates the researcher.src/lib/models/providers/anthropic/index.ts– loads chat and embedding models (loadChatModel,loadEmbeddingModel).src/lib/hooks/useChat.tsx– central hook that maintains chat state and talks to the model.
The UI components (src/components/**) render messages, settings and widgets (weather, stock, calculation) that are injected by the search agents.
How It Is Wired
Execution begins when Next.js routes a POST to /api/search. The handler in src/app/api/search/route.ts (function start at line 119) validates the request and calls src/lib/agents/search/index.ts → searchAsync.
searchAsync (line 13) creates a Researcher (src/lib/agents/search/researcher/index.ts) and invokes its research method. research (line 10) runs a registry of actions (src/lib/agents/search/researcher/actions/registry.ts) that eventually call search (in src/lib/agents/search/researcher/actions/webSearch.ts etc.).
search updates the chat block via src/lib/hooks/useChat.tsx → updateBlock, then calls the selected provider. For cloud models the path is:
searchAsync → research → streamText → this.openAIClient.chat.completions.create
For local Ollama models the call chain is:
searchAsync → research → streamText → src/lib/models/providers/ollama/ollamaLLM.ts → generateText
All database writes funnel through src/lib/db/index.ts (e.g., persisting chat history). File uploads are managed by src/lib/uploads/manager.ts and stored via src/lib/uploads/store.ts.
The most‑used internal symbols are useChat (called from 13 places) and saveConfig (8 callers). src/lib/agents/search/types.ts is a hub module with 17 inbound imports; changes there have a wide blast radius.
Circular imports involve 11 modules, notably src/lib/models/types.ts, src/components/ChatWindow.tsx, and src/lib/types.ts. These cycles increase build‑time fragility and make refactoring risky.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/Perplexica
cd Perplexica
# Install dependencies (Yarn is the declared manager)
yarn install
# Run locally with Next.js
yarn dev # starts the dev server on http://localhost:3000
# Or spin up the Docker composition (Dockerfile and docker‑compose.yaml are present)
docker compose up --build
Configuration files are expected under src/lib/config/ (e.g., clientRegistry.ts, serverRegistry.ts). The repository does not contain a sample .env – you must supply API keys for any external LLM provider (OpenAI, Anthropic, etc.) and the SearxNG endpoint as described in the docs (docs/API/SEARCH.md).
The API is reachable at POST /api/search with a JSON body matching SearchRequestBody (defined in src/lib/types.ts). The UI is served from the same Next.js process.
Real‑World Use
A corporate knowledge‑base portal could embed Perplexica as a micro‑service. An internal tool sends a user query to /api/search; the service fetches web results via SearxNG, runs them through a locally hosted Ollama LLM, and returns a markdown answer with citations. The front‑end component src/components/MessageRenderer/CodeBlock renders any code snippets, while src/components/Widgets/Weather.tsx displays weather widgets when relevant.
Code Health & Issues
- HIGH – Pin third‑party GitHub Actions to commit SHAs (
.github/workflows/docker-build.yaml). - HIGH – No test suite; 160 source files lack any test files.
- MEDIUM – GitHub token permissions are not declared (
docker-build.yaml). - MEDIUM – Dependabot/Renovate not configured (
package.jsonpresent, no bot). - MEDIUM – Docker base image not pinned by digest (
Dockerfile). - MEDIUM – No dependency‑vulnerability scan in CI.
- MEDIUM – Large binary (
.assets/demo.gif, 31 MB) should be moved to LFS or external storage. - LOW – CI jobs lack
timeout-minutes(docker-build.yaml).
Additional static findings: 11 import cycles, deep nesting in several UI components, a 712‑line useChat.tsx hook, duplicated route logic across API files, and an empty catch block in src/app/api/search/route.ts.
The Bottom Line
Perplexica provides a functional, privacy‑first AI answering stack with a clear separation between search, LLM providers and UI. It runs out‑of‑the‑box via Docker or Yarn, but the codebase suffers from missing tests, import cycles and several maintainability hotspots. It is suitable for teams that need a self‑hosted answer engine and are prepared to invest in refactoring and test coverage.