Skip to content

Latest commit

Β 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ” RepeatRadar

PhD Manuscript Repetition Detector & Fixer

Stop repeating yourself β€” let AI find it for you

Python FastAPI Streamlit Gemini License: MIT


🎯 The Problem

When writing a PhD thesis over months or years, the same ideas inevitably appear in multiple chapters, sometimes word-for-word, sometimes paraphrased. RepeatRadar finds these repetitions semantically (not just exact matches), scores them by severity, and lets you fix them with one click using Gemini AI.


✨ Features

  • πŸ“„ Upload PDF or DOCX β€” supports any academic document format
  • 🧠 Semantic detection β€” finds repetitions even when phrased differently
  • 🎯 Smart scoring β€” πŸ”΄ High / 🟑 Medium / 🟒 Low based on similarity, length, and signpost keywords
  • πŸ›  Three fix strategies β€” rewrite, shorten to recall sentence, or replace with cross-reference
  • βœ… Accept / Reject / Edit β€” you stay in control, AI just suggests
  • πŸ“₯ Export fixes β€” download a .txt changelog to apply to your Word/LaTeX document
  • ⚑ Caching β€” embeddings saved to disk, analysis is instant on repeat runs
  • πŸ’° 100% free β€” uses Gemini free tier, no paid API needed

πŸ— Architecture

PDF/DOCX Upload
    ↓
LangChain RecursiveCharacterTextSplitter β†’ tagged chunks
    ↓
Gemini gemini-embedding-001 β†’ FAISS FlatIP index (cached)
    ↓
FAISS search (top-K) β†’ cosine similarity pairs
    ↓
Filter: skip preamble / references / math / adjacent chunks
    ↓
Score: similarity + length + signpost keywords β†’ πŸ”΄/🟑/🟒
    ↓
Streamlit UI β€” side-by-side pair view
    ↓
Fix on demand β†’ Gemini Flash rewrite β†’ Accept / Reject

πŸ–₯ Screenshots

Upload Analyse Review & Fix
Drag & drop PDF/DOCX Real-time embedding progress bar Side-by-side pair comparison with fix panel

πŸš€ Quick Start

Prerequisites

1. Clone the repository

git clone https://github.com/bazzal99/RepeatRadar.git
cd RepeatRadar

2. Set up environment

cp .env.example .env
# Open .env and add your Gemini API key

3. Install dependencies

pip install -r requirements.txt
pip install streamlit==1.40.0 requests==2.32.3

4. Run the backend

cd backend
uvicorn app.main:app --reload --port 8080

5. Run the frontend (new terminal)

cd frontend
streamlit run app.py

6. Open the app

http://localhost:8501

πŸ€– Use as an MCP Server (Claude Desktop, Claude Code, etc.)

RepeatRadar's duplicate-detection pipeline (ingest β†’ embed β†’ detect β†’ score) is also exposed as a Model Context Protocol (MCP) tool (mcp_server/server.py), so any MCP-compatible client can call find_duplicates(file_path, doc_id=None) directly on a PDF or DOCX β€” no need to run the Streamlit UI. No changes were made to backend/app/services/*; the MCP server only imports and calls the functions that already exist there.

Tool exposed: find_duplicates(file_path, doc_id=None) β†’ returns a JSON report: total pairs found, counts per severity (high/medium/low), and the pairs themselves (chunk text truncated for readability).

Setup

cd mcp_server
python -m venv venv
source venv/bin/activate        # Windows: venv\Scripts\activate
pip install -r requirements.txt
export GOOGLE_API_KEY="your-gemini-api-key"   # Windows: $env:GOOGLE_API_KEY="..."

Why a separate requirements.txt? The mcp SDK requires pydantic>=2.12.0, while the FastAPI backend above is pinned to pydantic==2.8.2. Merging them into one file would force an untested pydantic upgrade on the core app, so mcp_server/ keeps its own isolated environment on purpose.

Run standalone (sanity check)

python server.py

A silent hang with no error means it started correctly β€” it's waiting for a client over stdio. Ctrl+C to stop.

Connect to Claude Desktop

Add to your claude_desktop_config.json (Settings β†’ Developer β†’ Local MCP servers β†’ Edit Config in Claude Desktop):

{
  "mcpServers": {
    "repeatradar": {
      "command": "/absolute/path/to/RepeatRadar/mcp_server/venv/bin/python",
      "args": ["/absolute/path/to/RepeatRadar/mcp_server/server.py"],
      "env": { "GOOGLE_API_KEY": "your-gemini-api-key" }
    }
  }
}

On Windows, use venv\Scripts\python.exe and escaped backslashes (\\) in the paths.

Restart Claude Desktop, then ask something like:

"Use repeatradar to find duplicates in C:\path\to\my_thesis.pdf"

Claude will call find_duplicates directly and report back the flagged pairs. First run on a new file embeds and caches it (mcp_server/faiss_store/<doc_id>.index); subsequent calls on the same file reuse the cache.

Troubleshooting

  • Path errors on the local server: the server sets its own working directory (os.chdir to its own folder) at startup specifically because MCP clients may launch it from an arbitrary cwd β€” if you fork this, keep that line.
  • Tool not visible in a chat: some Claude Desktop builds require enabling the connector per-conversation β€” check the "+" / Context panel near the message box.
  • "File not found": the path must be a real path on the machine running the server, not an uploaded/attached file in the chat β€” the server can't reach files that only exist inside the conversation.

🐳 Docker (Optional)

cp .env.example .env
# Add your API key to .env

cd infra
docker compose up -d

βš™οΈ Configuration

Edit RepeatRadar/.env:

# Embedding key (gemini-embedding-001)
GOOGLE_API_KEY=your_key_here

# Optional: separate key for fix agent to avoid quota conflicts
GOOGLE_API_KEY_GENERATION=your_second_key_here

# Detection sensitivity (0.92 = recommended, lower = more pairs found)
SIMILARITY_THRESHOLD=0.92

# Chunk size for splitting (characters)
CHUNK_SIZE=400
CHUNK_OVERLAP=80

Tip: Use two separate Gemini API keys β€” one for embeddings and one for the fix agent. This prevents quota conflicts since embedding and generation share the same free tier limits.


πŸ“ Project Structure

RepeatRadar/
β”œβ”€β”€ backend/
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ api/routes/
β”‚   β”‚   β”‚   β”œβ”€β”€ documents.py      # Upload & chunk endpoints
β”‚   β”‚   β”‚   └── analysis.py       # Embed, analyse, fix, accept endpoints
β”‚   β”‚   β”œβ”€β”€ core/
β”‚   β”‚   β”‚   └── config.py         # Settings from .env
β”‚   β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”‚   β”œβ”€β”€ ingestor.py       # PDF/DOCX β†’ LangChain chunks
β”‚   β”‚   β”‚   β”œβ”€β”€ embedder.py       # Gemini embeddings + FAISS cache
β”‚   β”‚   β”‚   β”œβ”€β”€ detector.py       # FAISS search + pair filtering
β”‚   β”‚   β”‚   β”œβ”€β”€ scorer.py         # πŸ”΄/🟑/🟒 scoring logic
β”‚   β”‚   β”‚   └── fixer.py          # Gemini Flash rewriting agent
β”‚   β”‚   └── main.py
β”‚   └── Dockerfile
β”œβ”€β”€ frontend/
β”‚   β”œβ”€β”€ app.py                    # Full Streamlit UI
β”‚   └── Dockerfile
β”œβ”€β”€ mcp_server/
β”‚   β”œβ”€β”€ server.py                 # MCP tool wrapping the detection pipeline
β”‚   β”œβ”€β”€ manifest.json             # Desktop Extension (.mcpb) manifest
β”‚   └── requirements.txt          # Separate env: mcp SDK needs pydantic>=2.12.0
β”œβ”€β”€ infra/
β”‚   └── docker-compose.yml
β”œβ”€β”€ .env.example
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ LICENSE
└── README.md

πŸ”¬ How Scoring Works

Each flagged pair is scored on three signals:

Signal Criteria Points
Similarity β‰₯ 0.95 (near-identical) +2
β‰₯ 0.90 (very similar) +1
β‰₯ 0.92 threshold 0
Length Both chunks β‰₯ 200 chars +2
One chunk β‰₯ 200 chars +1
Signpost "as introduced in", "recall that", etc. βˆ’2

Final score β†’ severity:

  • πŸ”΄ High (4-5 pts) β€” fix strongly recommended
  • 🟑 Medium (2-3 pts) β€” review recommended
  • 🟒 Low (0-1 pts) β€” likely intentional, shown collapsed

πŸ›  Fix Strategies

Strategy What it does Best for
Rewrite Different phrasing, same meaning Same-chapter repetition
Shorten Compress to one recall sentence Intro repeated in Conclusion
Reference Replace with pointer to original When repetition adds no value

πŸ§ͺ Tech Stack

Layer Technology
LLM + Embeddings Google Gemini (free tier)
Vector Index FAISS FlatIP
Chunking LangChain RecursiveCharacterTextSplitter
API FastAPI + Uvicorn
Frontend Streamlit
PDF parsing pypdf
DOCX parsing python-docx
Containers Docker Compose

πŸ‘€ Author

Mohammad Bazzal β€” ML Engineer | PhD Candidate in Telecommunications
IMT Atlantique, Lab-STICC, Brest, France

GitHub LinkedIn


πŸ“„ License

This project is licensed under the MIT License β€” see the LICENSE file for details.


Built to solve a real PhD problem β€” writing the same thing twice without realizing it.

About

PhD manuscript repetition detector & fixer, powered by Gemini AI. Finds semantic repetitions across chapters and suggests rewrites.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages