DEV Community

Cover image for CiteWise: a local research desk that will not invent a citation
Abishethvarman V
Abishethvarman V

Posted on AI-assisted

CiteWise: a local research desk that will not invent a citation

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle.

CiteWise is a local research desk for people who already have the documents: a student checking a reading pack, a researcher working from PDFs they uploaded, or a small practice that does not want those files sent to a hosted chat API. The question it is built to answer is simple. When the tool says something, can you open the page it came from?

There is no public demo URL. The app runs on your machine. The screenshots below are from a local run on 16 October 2026, using only the synthetic demo PDFs. Those files are fiction. They are labelled SYNTHETIC in the app.

What I Built

You upload PDFs. CiteWise extracts each page, keeps the page number from the PDF, and indexes the text. You can search without calling a chat model, or ask a question. The answer is supposed to point at a passage. If the passage is not there, or the model will not commit to one, the answer is marked insufficient and no quotation is invented.

A person would pay for the time they stop spending on a second pass: opening the PDF, finding the page, and checking whether a fluent sentence was actually in the file. That is the job. It is not a lawyer, and it is not a database of real authorities. A source match means the quotation was found in the extract. It does not mean the statement is legally correct, binding, or still good law.

The synthetic demo makes the limit visible. The Northwind demo says the oak timber arrived twelve days after the contractual date. Asked how many days that was, local llama3.2:3b answered "Not specified". CiteWise kept that answer, labelled it insufficient, and did not write "twelve days" into a citation the model had not made. The retrieved page still shows the sentence, so a reader can see both the evidence and the model's miss.

Landing page

Library, synthetic demo only

Search results

The PDF page that contains the twelve-days sentence

The local model answer, marked insufficient

Citation check: no evidence items, because the model cited none

Settings: local Ollama, no commercial API

Demo

Nothing is deployed. To run it locally you need Python 3.12, Node, and Ollama with a chat model pulled, for example ollama pull llama3.2:3b. Embeddings use nomic-embed-text when Ollama is up. If that model is missing, the app falls back to a deterministic hash and names that fallback in the interface. It does not switch to a commercial API.

cd backend
python -m venv .venv
.venv\Scripts\activate
pip install -e ".[dev]"
python -m app.cli seed-demo
uvicorn app.main:app --host 127.0.0.1 --port 8000
Enter fullscreen mode Exit fullscreen mode

In another terminal, from frontend, run npm install and npm run dev, then open the workspace and seed or upload the synthetic PDFs from the library. The screenshots in this post are the same files checked into docs/screenshots/.

Code

The repository is Abishethvarman/CiteWise.

The frontend is React, TypeScript, and Vite. The API is FastAPI under /api/v1, with SQLite, PyMuPDF for extraction, rank-BM25, and a local FAISS index. Chat goes to Ollama on 127.0.0.1:11434. The default tag in these screenshots is llama3.2:3b.

How I Built It

The open model is the generator, not a feature bolted on at the end. Search can run with no model at all. Asking a question retrieves passages first, then sends those passages to Ollama with a JSON schema. If Ollama is down, the answer is stored as model-unavailable. If the model returns JSON that does not match the schema, the app retries once and then stores invalid_model_output. It does not fill the gap with another provider.

Citation checking is a text check: the chunk has to exist, the page has to exist, the chunk has to have been shown to the model, and the quotation has to match the extract after normalisation. None of that is a legal conclusion.

I also kept a small labelled development fixture and a script that prints Recall@k, MRR, and nDCG. On that fixture, keyword retrieval scored 1.0 on eight synthetic queries. The file itself says it is a development set, not a legal benchmark. I am not treating that number as evidence that the product understands law.

Two DEV posts shaped how I scoped this. RAG Beyond the Demo by xinyangwuethz is blunt about citations and about not dressing up a demo as an evaluation. Picard OSS by iamsaurabhc is the closest neighbour I found: a local legal assistant that refuses to bluff. A self-hosted RAG stack without cloud AI APIs by pingredsai is the same constraint from the infrastructure side.

Why Does Open Innovation Matter?

A closed chat API would have been faster to wire up, and it would have sent the PDF text to someone else's server. The documents this tool is for are often the ones a person does not want to upload. Running the generator locally keeps that choice on the machine that already has the file.

Open weights also make the failure inspectable. I can name the tag (llama3.2:3b), show the retrieved page, and show that the model still said "Not specified". Swapping the tag does not require a new vendor account. I have not measured electricity or hardware cost against a hosted API, so I am not claiming this is cheaper. I am claiming the document does not have to leave the machine, and a bad citation is something you can open.

My Agent Session

I have not saved an agent session for this build yet. If I add one before the deadline, I will embed it here.

Prize Categories

I am entering the overall challenge only. This build does not integrate a partner product from the category list.

Top comments (0)