Skip to main content
Glama
Milflopper

memgrep

by Milflopper
README.md
# memgrep

Semantic search over your Telegram memes. Ask in Russian or English — find the meme you half-remember.

Pipeline: Telegram export → vision captions (bilingual) + OCR → multilingual embeddings (bge-m3, local) → PostgreSQL/pgvector hybrid search (dense + full-text, RRF) → cross-encoder reranking. Exposed as a CLI, a web UI and an MCP server, so your AI assistant can search your memes too.

See [the design spec](docs/superpowers/specs/2026-08-16-memgrep-design.md) for the full architecture.

## Quick start

```bash
cp .env.example .env   # fill in OpenRouter API key
make up                # PostgreSQL + pgvector on :5433
make test

uv run memgrep ingest                     # load data/result.json + photos into the db
uv run memgrep caption --limit 400       # describe images via a vision model (pilot)
uv run memgrep embed                      # local bge-m3 embeddings
uv run memgrep search "this is fine"     # CLI search; --open shows files in Preview
uv run memgrep serve                      # web UI on localhost:8000
uv run memgrep evals                      # golden-set metrics for the current config
uv run memgrep sync                       # ingest + caption + embed for new exports
uv run memgrep watch                      # follow the channel via Bot API
```

The Telegram export goes to `data/` (Telegram Desktop → Export chat history → Photos, JSON format). Memes and exported data stay local — `data/` is gitignored; only code is published.

## Search quality

Measured on a 21-query golden set collected through the UI feedback button ("это он ✓"), pilot corpus of 400 images. Each retrieval layer was added only after it proved itself:

| Layer | hit@5 | hit@10 | MRR |
|---|---|---|---|
| dense vectors only | 0.857 | 0.905 | 0.768 |
| + full-text search, RRF fusion | 0.905 | 0.952 | 0.815 |
| + cross-encoder reranker | 0.905 | **1.000** | **0.839** |

Captioning-model comparison on the same corpus and golden set: qwen3-vl-8b matched or beat qwen3-vl-235b on retrieval metrics at a third of the price, so the full corpus is indexed with the 8b model (~$3.6 for ~11k images). Caveat documented in the evals history: the golden set was collected on top of the 8b index, which biases the comparison toward it.

Search config is env-driven: `MEMGREP_SEARCH_MODE=vector|hybrid`, `MEMGREP_RERANK_ENABLED=true|false`. `memgrep evals` writes a timestamped JSON per configuration into `evals/results/` so runs stay comparable.

## MCP server

Let Claude (or any MCP client) search your memes:

```bash
claude mcp add memgrep -- uv run --directory /absolute/path/to/memgrep python -m memgrep.mcp_server
```

Tools: `search_memes(query, k)` returns matches with file paths and captions; `get_meme(sha256)` returns the image itself.

## Watch service

The `memgrep watch` command follows a Telegram channel in real-time via Bot API long polling. Set up the bot as a channel admin, configure `MEMGREP_TG_BOT_TOKEN` and `MEMGREP_TG_CHANNEL_ID`, and run the watcher service. New photos posted to the channel are automatically indexed: they get captioned, embedded, and searchable within minutes. Reactions on recent posts are also synced back to the database for engagement tracking.

## Stack

Python 3.12+, uv, PostgreSQL 17 + pgvector, sentence-transformers (BAAI/bge-m3 embeddings, BAAI/bge-reranker-v2-m3 reranker, both local), any OpenAI-compatible vision endpoint for captioning (default: OpenRouter, qwen3-vl), FastAPI, typer, MCP Python SDK. 49 tests, no network or model downloads in the test suite.

TDQS

A3.9/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct purposes: one performs semantic search over the collection, the other retrieves a specific image by hash. There is no overlap or ambiguity between them.

Naming Consistency5/5

Both tools follow the same verb_noun pattern in snake_case: search_memes and get_meme. The singular/plural variation is natural for resource action and does not break consistency.

Tool Count3/5

With only two tools, the server feels thin and sits at the low end of the borderline range. However, the narrow purpose of search and retrieval justifies a minimal surface, so it is not unreasonable.

Completeness5/5

For a read-only meme search and retrieval server, the surface is complete: search returns hashes and get_meme fetches the image. There are no dead ends or missing core operations within this defined scope.

Maintenance

ActivityMaintained
ResponsivenessNo issues