Architecture
A seam, not a stack.
Polymathy is deliberately one box in a larger diagram. It owns the fetch-and-chunk coordination between a metasearch engine and a content processor, and nothing else. This is what the deployment looks like, and where the boundaries are.
System diagram
┌───────────────────────────────────────────────┐
client ─▶│ Polymathy (actix-web 4, single binary) │
GET /v1/ │ │
search │ 1. query ──▶ SearxNG (SEARXNG_URL) │──▶ upstream engines
│ 2. top 10 URLs │
│ 3. fan out ─▶ Processor (PROCESSOR_URL) │──▶ chunk + embed
│ 4. assemble chunk_id → (source_url, text) │
└───────────────────────────────────────────────┘
│
▼
JSON chunk map ──▶ your LLM / rerank / UI Everything outside the box — SearxNG, the processor, the downstream LLM and UI — is supplied by you. Polymathy is the middle coordination layer.
The stack, as shipped
| Layer | Technology | Role |
|---|---|---|
| HTTP framework | actix-web 4 | Serves /v1/search and the API doc UIs; async on tokio. |
| OpenAPI | apistos | Generates the OpenAPI spec from the Rust handler types. |
| HTTP client | reqwest | Calls SearxNG and fans out to the content processor. |
| Vector primitives | usearch 2.12 | 384-dim, inner-product, F32-quantised index, per request. |
| Runtime | tokio | Concurrency for the parallel URL fan-out. |
The two contracts you implement
SearxNG (SEARXNG_URL)
Must answer GET /search?q=…&format=json with a results[] array of objects carrying a url. Any SearxNG instance with JSON output enabled satisfies this.
Content processor (PROCESSOR_URL)
Must accept a POST with a URL and a config block (chunking_size, chunking_type, embedding_model) and return chunks with embeddings. This is where fetching, boilerplate stripping, chunking, and embedding actually happen.
Because Polymathy owns neither contract's implementation, you can evolve your retrieval and embedding independently — a different SearxNG instance, a different chunker, a different embedding model — without redeploying Polymathy.