The MIRA research API. A Hono HTTP server backed by a BullMQ async worker that runs the full collection → extraction → clustering → synthesis pipeline. Self-host it; query it from anywhere.
POST /api/v1/research { query, depth?, sources? }
│
▼
BullMQ job enqueued (Redis)
│
▼ Worker picks up the job
┌─────┴──────────────────────────────────────────────┐
│ 1. Collectors run in parallel │
│ Reddit · HackerNews · RSS │
│ │
│ 2. Each item ingested into OpenViking (optional) │
│ │
│ 3. LLM extraction per item (concurrent) │
│ → pain_points, sentiment, category, key_quote │
│ │
│ 4. Embedding-based theme clustering │
│ → groups items by semantic similarity │
│ │
│ 5. Final synthesis report (LLM) │
│ → summary, painPoints, competitorWeaknesses, │
│ emergingGaps │
└────────────────────────────────────────────────────┘
│
▼
GET /api/v1/research/:jobId → ResearchResult
The fastest way to run this is via Docker Compose from the mira-core root:
git clone https://github.com/mira-js/mira-core.git
cd mira-core
cp .env.example .env # set OPENAI_API_KEY at minimum
docker compose upFor local development without Docker:
# From mira-core root
pnpm install && pnpm build
pnpm --filter @mira/api-core migrate
pnpm --filter @mira/api-core start| Variable | Required | Default | Description |
|---|---|---|---|
OPENAI_API_KEY |
Yes | — | LLM provider API key |
OPENAI_BASE_URL |
No | DeepSeek | Any OpenAI-compatible base URL |
OPENAI_MODEL |
No | deepseek-chat |
Model to use for extraction and synthesis |
DATABASE_URL |
Yes | — | PostgreSQL connection string |
REDIS_URL |
Yes | — | Redis connection string |
PORT |
No | 3000 |
HTTP listen port |
DASHBOARD_URL |
No | http://localhost:5173 |
CORS allowed origin |
REDDIT_CLIENT_ID |
No | — | Reddit OAuth (improves rate limits) |
REDDIT_CLIENT_SECRET |
No | — | Reddit OAuth |
REDDIT_USERNAME |
No | — | Reddit OAuth |
REDDIT_PASSWORD |
No | — | Reddit OAuth |
JINA_API_KEY |
No | — | Enables full-text article extraction |
MIRA_ENABLE_FULLTEXT |
No | false |
Set true to fetch article bodies via Jina |
MIRA_EXTRACTION_CONCURRENCY |
No | 5 |
Parallel LLM calls during extraction phase |
MIRA_OPENVIKING_INGEST_CONCURRENCY |
No | 10 |
Parallel writes to OpenViking |
MIRA_PROMPTS_DIR |
No | ./prompts |
Directory for custom prompt overrides |
OPENVIKING_URL |
No | — | OpenViking base URL (semantic search, optional) |
OPENVIKING_API_KEY |
No | — | OpenViking API key (optional) |
Enqueue a new research job.
Request body:
{
query: string // required
depth?: "quick" | "deep" // default: "quick"
sources?: string[] // default: ["reddit","hackernews","news"]
}Response 202 Accepted:
{ "jobId": "abc123", "status": "queued" }Depth behaviour:
depth |
Reddit limit | HN limit | Notes |
|---|---|---|---|
quick |
25/subreddit | 20 | Fast, ~30–60 s total |
deep |
50/subreddit | 40 | More coverage, ~60–120 s |
Poll for job status and results.
Response:
{
jobId: string
status: "queued" | "active" | "completed" | "failed"
progress?: number // 0–100, present while active
createdAt: string // ISO 8601
result?: ResearchResult // present when status === "completed"
}ResearchResult shape:
{
query: string
summary: string
painPoints: PainPointTheme[]
competitorWeaknesses: PainPointTheme[]
emergingGaps: PainPointTheme[]
rawItems: CollectedItem[]
}
// PainPointTheme
{
theme: string
frequency: number
sources: string[]
sentiment: number // -1.0 to 1.0
evidence: { source: string; url: string; excerpt: string }[]
}List recent jobs (latest first).
{ "status": "ok", "timestamp": "2024-01-01T00:00:00.000Z" }Three prompt templates drive the LLM pipeline:
| File | Phase |
|---|---|
prompts/categorize_content.txt |
First-pass relevance classification |
prompts/extract_pain_points.txt |
Structured per-item extraction |
prompts/synthesize_report.txt |
Final cross-item synthesis |
Override any or all by setting MIRA_PROMPTS_DIR to your own directory. Missing files fall back to the bundled defaults — only ship the files you want to change.
MIRA_PROMPTS_DIR=/path/to/my-promptsThe pipeline uses the OpenAI SDK with a configurable base URL, so it works with any provider that implements the OpenAI chat completions API:
| Provider | OPENAI_BASE_URL |
OPENAI_MODEL |
|---|---|---|
| DeepSeek (default, cheapest) | https://api.deepseek.com |
deepseek-chat |
| OpenAI | (omit) | gpt-4o-mini |
| Groq | https://api.groq.com/openai/v1 |
llama-3.3-70b-versatile |
| Ollama (local) | http://localhost:11434/v1 |
llama3.2 |
A single PostgreSQL table (research_jobs) stores job metadata. Run the migration before first start:
pnpm --filter @mira/api-core migrate