# lazyfetch/locus architecture

> Architecture diagram of the GitHub repository lazyfetch/locus, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-25). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/lazyfetch/locus
- Repository: https://github.com/lazyfetch/locus

## Overview

Locus is a conversational mutual-fund research application. A user submits a question in the chat UI; the backend plans and runs structured and document retrieval, assembles a token-budgeted context with conversation history, and asks an LLM to produce an answer with sources. The tree also shows fund-data ingestion and separate entity-resolution/NER capabilities. The README is empty, and excerpts are partial, so some endpoint wiring and unsampled subsystem relationships are intentionally not inferred.

Main entry points: [`backend/src/main/java/com/lazyfetch/locus/search/rag/RagService.java`](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/rag/RagService.java) (RAG orchestration), [`backend/src/main/java/com/lazyfetch/locus/search/hybrid/HybridSearchService.java`](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/hybrid/HybridSearchService.java) (Hybrid retrieval), [`frontend/src/pages/ChatPage.tsx`](https://github.com/lazyfetch/locus/blob/main/frontend/src/pages/ChatPage.tsx) (Chat interface), [`backend/src/main/java/com/lazyfetch/locus/search/pgvector/PgVectorService.java`](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/pgvector/PgVectorService.java) (Vector search)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_experience["Chat experience"]
  node_chat["Chat interface<br/>[ChatPage.tsx]"]
  node_chat_ui["Chat components<br/>[MessageInput.tsx]"]
end

subgraph group_answering["Question answering"]
  node_search_api["Search API"]
  node_rag["RAG orchestration<br/>[RagService.java]"]
  node_budget["Context budgeting"]
  node_compress["Context compression"]
  node_assemble["Context assembly"]
  node_memory["Conversation memory<br/>[MemoryManager.java]"]
  node_conversations["Conversation state"]
  node_llm_client["LLM client<br/>[LlmClient.java]"]
end

subgraph group_retrieval["Fund retrieval"]
  node_hybrid["Hybrid retrieval"]
  node_planner["Query planning"]
  node_pgvector["Vector search"]
  node_embedding["Query embeddings"]
  node_lucene["Chunk text search"]
end

subgraph group_data["Fund data"]
  node_mf_data["Fund data access<br/>[MfDataService.java]"]
  node_fund_resolver["Fund resolution<br/>[FundResolver.java]"]
  node_entity_linker["Entity linking<br/>[EntityLinker.java]"]
  node_ner["Named entities<br/>[NerService.java]"]
  node_postgres[("PostgreSQL data")]
end

subgraph group_ingestion["Data ingestion"]
  node_pipeline["Fund data pipeline"]
end

node_user(("Research user"))
node_openrouter["OpenRouter"]

node_user -->|"asks"| node_chat
node_chat -->|"renders"| node_chat_ui
node_chat -->|"submits query"| node_search_api
node_search_api -->|"invokes"| node_rag
node_rag -->|"reads history"| node_conversations
node_rag -->|"creates conversation"| node_conversations
node_rag -->|"retrieves results"| node_hybrid
node_rag -->|"allocates tokens"| node_budget
node_rag -->|"gets prompt history"| node_memory
node_rag -->|"compresses results"| node_compress
node_rag -->|"assembles context"| node_assemble
node_rag -->|"requests answer"| node_llm_client
node_llm_client -->|"calls model"| node_openrouter
node_hybrid -->|"plans query"| node_planner
node_hybrid -->|"queries fund data"| node_mf_data
node_hybrid -->|"searches vectors"| node_pgvector
node_hybrid -->|"searches chunks"| node_lucene
node_pgvector -->|"embeds query"| node_embedding
node_pgvector -->|"queries vectors"| node_postgres
node_lucene -->|"loads chunk rows"| node_postgres
node_mf_data -->|"queries fund tables"| node_postgres
node_pipeline -->|"loads fund data"| node_postgres
node_fund_resolver -->|"resolves funds"| node_mf_data
node_entity_linker -->|"links entities"| node_fund_resolver
node_entity_linker -->|"extracts entities"| node_ner

click node_chat "https://github.com/lazyfetch/locus/blob/main/frontend/src/pages/ChatPage.tsx"
click node_chat_ui "https://github.com/lazyfetch/locus/blob/main/frontend/src/components/MessageInput.tsx"
click node_search_api "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/controller/SearchController.java"
click node_rag "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/rag/RagService.java"
click node_budget "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/context/ContextBudgetAllocator.java"
click node_compress "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/context/ContextCompressor.java"
click node_assemble "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/context/ContextAssembler.java"
click node_memory "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/conversation/MemoryManager.java"
click node_conversations "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/conversation/ConversationService.java"
click node_llm_client "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/llm/LlmClient.java"
click node_hybrid "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/hybrid/HybridSearchService.java"
click node_planner "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/planner/MfQueryPlanner.java"
click node_pgvector "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/pgvector/PgVectorService.java"
click node_embedding "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/embedding/EmbeddingService.java"
click node_lucene "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/lucene/LuceneChunkService.java"
click node_mf_data "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/data/MfDataService.java"
click node_fund_resolver "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/data/FundResolver.java"
click node_entity_linker "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/data/EntityLinker.java"
click node_ner "https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/ner/NerService.java"
click node_pipeline "https://github.com/lazyfetch/locus/tree/main/data_pipeline"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_chat,node_chat_ui,node_user toneBlue
class node_search_api,node_rag,node_budget,node_compress,node_assemble,node_memory,node_conversations,node_llm_client toneAmber
class node_hybrid,node_planner,node_pgvector,node_embedding,node_lucene,node_openrouter toneMint
class node_mf_data,node_fund_resolver,node_entity_linker,node_ner,node_postgres toneRose
class node_pipeline toneIndigo
```

## Components

### Chat experience

- [Chat interface](https://github.com/lazyfetch/locus/blob/main/frontend/src/pages/ChatPage.tsx): `frontend/src/pages/ChatPage.tsx`
- [Chat components](https://github.com/lazyfetch/locus/blob/main/frontend/src/components/MessageInput.tsx): `frontend/src/components/MessageInput.tsx`

### Question answering

- [Search API](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/controller/SearchController.java): `backend/src/main/java/com/lazyfetch/locus/search/controller/SearchController.java`
- [RAG orchestration](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/rag/RagService.java): `backend/src/main/java/com/lazyfetch/locus/search/rag/RagService.java`
- [Context budgeting](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/context/ContextBudgetAllocator.java): `backend/src/main/java/com/lazyfetch/locus/search/context/ContextBudgetAllocator.java`
- [Context compression](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/context/ContextCompressor.java): `backend/src/main/java/com/lazyfetch/locus/search/context/ContextCompressor.java`
- [Context assembly](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/context/ContextAssembler.java): `backend/src/main/java/com/lazyfetch/locus/search/context/ContextAssembler.java`
- [Conversation memory](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/conversation/MemoryManager.java): `backend/src/main/java/com/lazyfetch/locus/search/conversation/MemoryManager.java`
- [Conversation state](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/conversation/ConversationService.java): `backend/src/main/java/com/lazyfetch/locus/search/conversation/ConversationService.java`
- [LLM client](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/llm/LlmClient.java): `backend/src/main/java/com/lazyfetch/locus/search/llm/LlmClient.java`

### Fund retrieval

- [Hybrid retrieval](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/hybrid/HybridSearchService.java): `backend/src/main/java/com/lazyfetch/locus/search/hybrid/HybridSearchService.java`
- [Query planning](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/planner/MfQueryPlanner.java): `backend/src/main/java/com/lazyfetch/locus/search/planner/MfQueryPlanner.java`
- [Vector search](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/pgvector/PgVectorService.java): `backend/src/main/java/com/lazyfetch/locus/search/pgvector/PgVectorService.java`
- [Query embeddings](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/embedding/EmbeddingService.java): `backend/src/main/java/com/lazyfetch/locus/search/embedding/EmbeddingService.java`
- [Chunk text search](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/lucene/LuceneChunkService.java): `backend/src/main/java/com/lazyfetch/locus/search/lucene/LuceneChunkService.java`

### Fund data

- [Fund data access](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/data/MfDataService.java): `backend/src/main/java/com/lazyfetch/locus/search/data/MfDataService.java`
- [Fund resolution](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/data/FundResolver.java): `backend/src/main/java/com/lazyfetch/locus/search/data/FundResolver.java`
- [Entity linking](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/data/EntityLinker.java): `backend/src/main/java/com/lazyfetch/locus/search/data/EntityLinker.java`
- [Named entities](https://github.com/lazyfetch/locus/blob/main/backend/src/main/java/com/lazyfetch/locus/search/ner/NerService.java): `backend/src/main/java/com/lazyfetch/locus/search/ner/NerService.java`
- **PostgreSQL data**

### Data ingestion

- [Fund data pipeline](https://github.com/lazyfetch/locus/tree/main/data_pipeline): `data_pipeline`

### Other components

- **Research user**
- **OpenRouter**

## Connections

- Research user → Chat interface: asks
- Chat interface → Chat components: renders
- Chat interface → Search API: submits query
- Search API → RAG orchestration: invokes
- RAG orchestration → Conversation state: reads history
- RAG orchestration → Conversation state: creates conversation
- RAG orchestration → Hybrid retrieval: retrieves results
- RAG orchestration → Context budgeting: allocates tokens
- RAG orchestration → Conversation memory: gets prompt history
- RAG orchestration → Context compression: compresses results
- RAG orchestration → Context assembly: assembles context
- RAG orchestration → LLM client: requests answer
- LLM client → OpenRouter: calls model
- Hybrid retrieval → Query planning: plans query
- Hybrid retrieval → Fund data access: queries fund data
- Hybrid retrieval → Vector search: searches vectors
- Hybrid retrieval → Chunk text search: searches chunks
- Vector search → Query embeddings: embeds query
- Vector search → PostgreSQL data: queries vectors
- Chunk text search → PostgreSQL data: loads chunk rows
- Fund data access → PostgreSQL data: queries fund tables
- Fund data pipeline → PostgreSQL data: loads fund data
- Fund resolution → Fund data access: resolves funds
- Entity linking → Fund resolution: links entities
- Entity linking → Named entities: extracts entities
