⚡ GraphSearch
Ask questions. Get grounded answers. One GraphQL endpoint.
Turn any pile of documents — PDFs, Markdown, plain text — into a typed,
introspectable Q&A API. Zero API keys, zero infrastructure to start.
Quickstart · Playground · API · Architecture · Roadmap · Contributing
"How do I get my money back?" finds the returns policy — no shared keywords, no API keys.
✨ Why GraphSearch?
| 🔍 Semantic search, zero keys | Ships with offline embeddings — pip install and ask questions. Add sentence-transformers for real semantic retrieval, still 100% local. |
| 📎 Grounded, cited answers | Answers cite their sources as [1], [2], … mapping 1:1 to returned chunks with relevance scores. No hallucination hand-waving. |
| 📄 PDFs welcome | Drop PDF / Markdown / text files into the Playground, the uploadFile mutation, or the CLI — text extraction is built in. |
| 🧬 One typed endpoint | GraphQL means clients fetch exactly the fields they need, with introspection, GraphiQL, and a generated TypeScript client that fails CI if it drifts from the schema. |
| 🔌 Pluggable everything | Embedder, vector store, and LLM are clean interfaces. Swap OpenAI ↔ Claude ↔ offline modes with one env var. |
| 🪶 No infrastructure | SQLite + numpy under the hood. No vector DB cluster, no queue, no Redis — until you decide you need one. |
🚀 Quickstart
pip install graphsearch-rag # imports as `graphsearch`
graphsearch-ingest data/example_docs # or your own .pdf / .md / .txt
graphsearch # → http://localhost:8000
That's it — no API keys, no services, no config. Prefer containers?
docker run -p 8000:8000 -v graphsearch-data:/data ghcr.io/mohithgowdak/graphsearch:latest
The image runs as a non-root user, ships a HEALTHCHECK against /health, and
persists its SQLite database in the /data volume. It bundles the openai,
anthropic, and nltk extras, so every chunking strategy except
sentence_spacy works offline out of the box. Invalid configuration (for
example a CHUNK_OVERLAP larger than CHUNK_SIZE, or an unknown chunking
strategy) fails at startup with an explicit message rather than on the first
upload.
graphsearchnot recognized? Withpip install --user(Windows default when site-packages isn't writable), pip's Scripts folder may not be on PATH. Skip PATH entirely:python -m graphsearch # start the server python -m graphsearch.ingest data/example_docs # ingest documents
🎮 The Playground
Open http://localhost:8000/ and test RAG with your own documents before writing a line of client code — paste text or drop files, ask questions, inspect ranked sources. Every action has a "Show the GraphQL" toggle revealing the exact query it runs, ready to copy into your app.
Prefer raw GraphQL? GraphiQL lives at http://localhost:8000/graphql.
🎚 Level up the pipeline
The default mode is fully offline (hashing-trick embeddings + extractive answers) so the whole pipeline runs anywhere, including CI. Upgrade each stage independently:
Real semantic search — still no API key (sentence-transformers, ~80 MB model, CPU):
pip install "graphsearch-rag[local]"
export GRAPHSEARCH_EMBEDDINGS=local # $env:GRAPHSEARCH_EMBEDDINGS='local' on Windows
graphsearch-ingest data/example_docs # re-ingest: embeddings are created at ingest time
graphsearch
LLM-generated answers with citations (OpenAI or Anthropic):
pip install "graphsearch-rag[anthropic]" # or [openai]
export GRAPHSEARCH_LLM=anthropic # or openai
export ANTHROPIC_API_KEY=sk-ant-...
graphsearch
| Setting | Options | Default |
|---|---|---|
GRAPHSEARCH_EMBEDDINGS |
hash (offline) · local (offline, semantic) · openai |
hash |
GRAPHSEARCH_LLM |
extractive (offline) · openai · anthropic |
extractive |
Documents are embedded at ingest time — re-ingest after switching embedding backends. Full configuration reference: .env.example
🧩 GraphQL API
# Queries
answer(question: String!, topK: Int, expand: Int): Answer! # RAG + optional neighbor expansion
search(query: String!, topK: Int, expand: Int): [Chunk!]!
documents(limit: Int = 20, offset: Int = 0): [Document!]!
document(id: ID!): Document
# Mutations
uploadDocument(content: String!, title: String, source: String, chunking: ChunkingStrategy): Document!
uploadFile(file: Upload!, title: String, source: String, chunking: ChunkingStrategy): Document!
deleteDocument(id: ID!): Boolean!
Example: ingest and ask in one session
mutation {
uploadDocument(
content: "Support hours are 9am-5pm PST, Monday through Friday."
title: "support-hours"
) { id chunkCount }
}
query {
answer(question: "When is support available?") {
text # cites sources as [1], [2], ...
sources { documentTitle text score }
}
}
File uploads (multipart)
uploadFile follows the GraphQL multipart request spec.
PDF text extraction happens server-side via pypdf — scanned/image-only PDFs are
rejected with a hint to OCR them first, encrypted PDFs with a hint to decrypt.
TypeScript? clients/typescript ships a fully-typed
SDK generated straight from this schema — client.Answer({ question }),
client.Search(...), etc. CI regenerates it on every push and fails the build
if it drifts from the server.
🏗 Architecture
flowchart LR
C([Client]) -->|"GraphQL"| S["FastAPI + Strawberry"]
S --> R["RagService"]
R --> CH["Chunker<br/>paragraph · fixed · recursive · …"]
R --> E["Embedder<br/>hash · local · OpenAI"]
R --> V["VectorStore<br/>in-memory cosine"]
R --> L["LLM<br/>extractive · OpenAI · Claude"]
V <--> DB[("SQLite<br/>docs · chunks · vectors")]
style S fill:#e10098,color:#fff
style R fill:#1c1c28,color:#fff
Every pipeline stage is an abstract interface (Chunker, Embedder, VectorStore, LLM) —
new backends are drop-in additions, and several are up for grabs as
good first issues.
Chunking strategies
Set GRAPHSEARCH_CHUNKING (or pass chunking on uploadDocument / uploadFile) to pick how documents are split. Default paragraph is the original packer. Also available: fixed, sentence, recursive, markdown, html, latex, semantic (uses the configured embedder), sentence_nltk / sentence_spacy / topic_lda (optional extras), and contextual (requires GRAPHSEARCH_LLM=openai or anthropic). Query-time neighbor expansion is expand on search / answer (or GRAPHSEARCH_CHUNK_EXPAND).
topic_lda needs pip install graphsearch-rag[lda] (scikit-learn). It treats each
sentence as a mini-document, fits Latent Dirichlet Allocation over the document,
and starts a new chunk wherever the dominant topic changes. Because the model is
fit per document rather than across a corpus, it needs a reasonably long text to
say anything useful — under six sentences it falls back to plain sentence
packing, and single-sentence topic flips are smoothed away so one noisy label
can't fragment a passage. Tune it with GRAPHSEARCH_LDA_TOPICS (default 5,
capped at half the sentence count) and GRAPHSEARCH_LDA_MAX_ITER (default 10).
The seed is fixed, so re-ingesting a document yields identical chunks. Expect it
to be the slowest strategy on large documents, since every ingest fits a model.
🛠 Development
git clone https://github.com/mohithgowdak/graphsearch && cd graphsearch
pip install -e ".[dev]"
ruff check . # lint
pytest -v # offline suite (hash embedder + extractive LLM)
🗺 Roadmap / good first issues
- Vector store backends: Qdrant, Weaviate, Redis/Valkey, pgvector, FAISS
- Embedding backends: Cohere, Voyage
- Streaming answers via GraphQL subscriptions
- Metadata tags + filters; hybrid keyword+vector search (SQLite FTS5)
- Auth (API keys / JWT) and rate limiting
- Query/embedding caching
- Auto-generated TypeScript client — see
clients/typescript - Evaluation harness + Prometheus metrics
- Advanced RAG: query rewriting, multi-hop retrieval, citation spans
Most of these are filed with implementation notes — grab one 👋
🤝 Contributing
Contributions are very welcome! CONTRIBUTING.md covers setup,
the backend-authoring guide, and PR guidelines. Look for the
good first issue
label to get started.
📄 License
MIT — do whatever you want, just keep the notice.
Metadata
Release files for graphsearch-rag 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| graphsearch_rag-0.5.0.tar.gz | 40.7 kB | With provenance | Details for graphsearch_rag-0.5.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| graphsearch_rag-0.5.0-py3-none-any.whl | Python 3 | none | any | Details for graphsearch_rag-0.5.0-py3-none-any.whl |
Total release size: 77.0 kB
Release files / graphsearch_rag-0.5.0.tar.gz
| Download URL | graphsearch_rag-0.5.0.tar.gz |
|---|---|
| Size | 40.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
eec4b63cce05e41a5f295b0b5405ae1d08930cdaa683977ea124bc3861fa71f4
|
|
BLAKE2b-256 checksum How to use checksums |
6be6dedb28aabc3f30500b050aae75806ae550e4d2476226fc818dd28ab1fa6f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 22, 2026.
Transparency logRelease files / graphsearch_rag-0.5.0-py3-none-any.whl
| Download URL | graphsearch_rag-0.5.0-py3-none-any.whl |
|---|---|
| Size | 36.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1e6d9a35216985446ebcabc0866372310ed3a66f157bd0a5ef2d5907e8bf5714
|
|
BLAKE2b-256 checksum How to use checksums |
18e049873ad0b7594a861b489a821cc61dbf552d564b4cdec2b6c35debf69c08
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 22, 2026.
Transparency logSecurity for graphsearch-rag 0.5.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for graphsearch_rag-0.5.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 22, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.