A lightweight, asynchronous research pipeline that plans, searches, summarizes, critiques, and delivers structured markdown reports — powered by remote LLM APIs with no local model serving required.
Key capabilities:
- Autonomous planning — decomposes queries into targeted sub-questions
- Parallel search — bounded async retrieval with provider fallback
- Iterative refinement — critic agent loops until research is sufficient or iteration ceiling is reached
- Streaming output — real-time NDJSON events for live frontend updates
- Zero heavy infrastructure — no Redis, no message queue, no local model serving
MARS orchestrates a five-stage pipeline via LangGraph:
Query → Planner → Search → Summarizer → Critic → (loop or Finalize)
| Agent | Module | Responsibility |
|---|---|---|
| Planner | app/agents/planner.py |
Generates focused sub-questions from the original query |
| Search | app/agents/search.py |
Executes bounded async searches with provider fallback |
| Summarizer | app/agents/summarizer.py |
Extracts structured facts from raw search snippets |
| Critic | app/agents/critic.py |
Evaluates research sufficiency; triggers refinement or finalization |
| Workflow | app/graph/workflow.py |
Defines graph transitions and loop routing logic |
Supporting components:
app/api/routes.py— FastAPI endpoints including the streaming research routeapp/db/sqlite.py— SQLite initialization and report persistenceapp/core/— Configuration, logging, and shared utilities
| Layer | Technology |
|---|---|
| Backend framework | FastAPI |
| Agent orchestration | LangGraph |
| HTTP client | httpx (async) |
| Database | aiosqlite / SQLite |
| Data validation | Pydantic |
| Frontend | React 18 + Vite 5 |
mars/
├── main.py # Application entrypoint
├── requirements.txt
├── .env.example
├── app/
│ ├── agents/
│ │ ├── planner.py
│ │ ├── search.py
│ │ ├── summarizer.py
│ │ └── critic.py
│ ├── api/
│ │ └── routes.py
│ ├── core/
│ ├── db/
│ │ └── sqlite.py
│ └── graph/
│ └── workflow.py
└── ui/
└── src/
| Requirement | Minimum Version |
|---|---|
| Python | 3.10+ |
| Node.js | 18+ |
| npm | 9+ |
LLM Provider: At least one of
GROQ_API_KEYorHUGGINGFACE_API_KEYmust be configured.
git clone <repository-url>
cd marspython3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .envOpen .env and set at least one LLM provider key:
# Primary LLM (recommended)
GROQ_API_KEY=your_groq_key_here
# Fallback LLM
HUGGINGFACE_API_KEY=your_hf_key_here
# Optional: richer search results
TAVILY_API_KEY=your_tavily_key_hereSee the full Configuration Reference below.
uvicorn main:app --host 127.0.0.1 --port 8000 --reloadcd ui
npm install
npm run devThe application will be available at http://127.0.0.1:5173.
| Variable | Required | Default | Description |
|---|---|---|---|
GROQ_API_KEY |
Conditional* | "" |
Primary LLM provider API key |
GROQ_MODEL |
No | llama-3.1-8b-instant |
Groq model identifier |
HUGGINGFACE_API_KEY |
Conditional* | "" |
Fallback LLM provider API key |
HUGGINGFACE_MODEL |
No | Qwen/Qwen2.5-7B-Instruct |
HuggingFace model identifier |
TAVILY_API_KEY |
No | "" |
Optional enriched search provider |
DATABASE_URL |
No | ./research.db |
SQLite database file path |
MAX_PARALLEL_SEARCH |
No | 2 |
Maximum concurrent search operations |
MAX_ITERATIONS |
No | 3 |
Maximum planner–search–summarize–critic loop cycles |
LLM_TIMEOUT_SEC |
No | 25 |
Per-request LLM timeout (seconds) |
SEARCH_TIMEOUT_SEC |
No | 20 |
Per-request search timeout (seconds) |
* At least one of
GROQ_API_KEYorHUGGINGFACE_API_KEYmust be provided.
Returns service metadata and a list of available endpoints.
Health check endpoint.
Response
{ "status": "ok" }Executes the full research pipeline and streams progress as newline-delimited JSON (application/x-ndjson).
Request body
{
"query": "What are the latest open-source small language model benchmarks in 2026?"
}Validation
querymust be between 5 and 500 characters.
Event schema
| Event type | Payload |
|---|---|
progress |
{ "type": "progress", "request_id": "...", "message": "..." } |
plan |
{ "type": "plan", "items": ["..."] } |
search_progress |
{ "type": "search_progress", "snippets": 12 } |
critic |
{ "type": "critic", "iteration": 1, "reason": "..." } |
findings |
{ "type": "findings", "items": [{ "claim": "...", "source": "..." }] } |
final_report |
{ "type": "final_report", "report": "...", "confidence": 0.73 } |
error |
{ "type": "error", "message": "..." } |
Example
curl -N -X POST http://127.0.0.1:8000/api/research/stream \
-H "Content-Type: application/json" \
-d '{"query": "Compare efficient open-source speech-to-text models for CPU inference"}'Completed research reports are automatically persisted to a local SQLite database (research.db by default).
Table: research_reports
| Column | Type | Description |
|---|---|---|
query |
TEXT | The original research query |
report |
TEXT | Final markdown report content |
confidence |
REAL | Model-assigned confidence score (0.0–1.0) |
created_at |
TEXT | UTC timestamp in ISO-8601 format |
MARS is designed to operate efficiently in constrained environments:
- Async I/O throughout the API, search, and workflow layers
- Bounded concurrency via
MAX_PARALLEL_SEARCHto cap simultaneous search requests - Iteration ceiling via
MAX_ITERATIONSto prevent unbounded agent loops - Provider fallback for graceful degradation when a primary LLM is unavailable
- Minimal footprint — no Redis, no external queue, no local model inference
- Never commit real API keys to source control
- Keep
.envlocal — add it to.gitignore - Rotate keys immediately if they are accidentally exposed
- In production, use a secrets manager rather than a flat
.envfile
- The Vite dev server proxies
/apirequests tohttp://127.0.0.1:8000— configured inui/vite.config.js - CORS is enabled for
http://127.0.0.1:5173andhttp://localhost:5173 - Backend hot-reload is enabled by default via
--reloadflag in theuvicornstart command
