Skip to main content

⚡ 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.

CI PyPI Python Docker License: MIT

Quickstart · Playground · API · Architecture · Roadmap · Contributing

GraphSearch demo: asking a question in GraphiQL and getting a grounded answer with ranked sources

"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.

graphsearch not recognized? With pip 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.

The GraphSearch Playground: your documents on the left, grounded answers with ranked sources on the right

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.


If GraphSearch saved you from wiring up a RAG pipeline by hand, consider giving it a ⭐ — it helps others find it.

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)

Source distribution for graphsearch-rag 0.5.0
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)

Table of built distributions (wheels) for graphsearch-rag 0.5.0
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page