{"id":50667370,"url":"https://github.com/surinderlohat/searchkit","last_synced_at":"2026-06-08T07:35:52.931Z","repository":{"id":345717940,"uuid":"1186984429","full_name":"surinderlohat/searchkit","owner":"surinderlohat","description":"Plug-in vector search for any stack — one container, zero config, production ready.","archived":false,"fork":false,"pushed_at":"2026-03-20T15:57:04.000Z","size":235,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-21T02:28:42.321Z","etag":null,"topics":["chroma","chromadb","docker-image","fastapi","vector","vector-database","vectors"],"latest_commit_sha":null,"homepage":"https://www.linkedin.com/in/surinder-singh-lohat","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/surinderlohat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-20T07:54:51.000Z","updated_at":"2026-03-20T14:45:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/surinderlohat/searchkit","commit_stats":null,"previous_names":["surinderlohat/chroma-db"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/surinderlohat/searchkit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/surinderlohat%2Fsearchkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/surinderlohat%2Fsearchkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/surinderlohat%2Fsearchkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/surinderlohat%2Fsearchkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/surinderlohat","download_url":"https://codeload.github.com/surinderlohat/searchkit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/surinderlohat%2Fsearchkit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34053435,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chroma","chromadb","docker-image","fastapi","vector","vector-database","vectors"],"created_at":"2026-06-08T07:34:59.986Z","updated_at":"2026-06-08T07:35:52.923Z","avatar_url":"https://github.com/surinderlohat.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SearchKit\n\n\u003e **Plug-in vector search for any stack — one container, zero config, production ready.**\n\nA lightweight FastAPI service with **embedded ChromaDB** that gives your app semantic search via simple REST endpoints. No separate vector DB container, no complex setup — just drop it in, point your service at it, and search.\n\n---\n\n## What Can You Build With This?\n\nThis service acts as a **dedicated semantic search layer** that sits alongside your main application. Here are some real-world use cases:\n\n🔍 **AI-Powered Search** — replace keyword search in your app with meaning-based search. Users search for _\"comfortable running shoes\"_ and find relevant products even if the description says _\"lightweight athletic footwear\"_.\n\n🤖 **RAG (Retrieval-Augmented Generation)** — feed relevant context to your LLM before generating a response. Store your documents here, search by query, pass top results to GPT/Claude as context.\n\n📄 **Document Similarity** — find related articles, tickets, or records. _\"Show me support tickets similar to this one\"_ or _\"find blog posts related to this topic\"_.\n\n🛒 **Product Recommendations** — embed product descriptions and find semantically similar items. _\"Customers who viewed this also liked...\"_ without collaborative filtering.\n\n💬 **FAQ \u0026 Chatbot Matching** — match user questions to the closest FAQ entry or support article, even when the wording is completely different.\n\n🏷️ **Smart Tagging \u0026 Categorisation** — automatically classify incoming content by comparing it against category embeddings.\n\n---\n\n![Swagger UI](./assets/swagger-ui.png)\n\n## Architecture\n\n```\nYour Main Service\n      │\n      ▼  HTTP\nsearchkit (FastAPI :9000)\n   └── ChromaDB (embedded, in-process)\n         │\n         ▼\n   chroma-data (Docker volume)\n```\n\n## Quick Start\n\n```bash\ndocker compose up -d\n```\n\n| Service     | URL                        |\n| ----------- | -------------------------- |\n| Wrapper API | http://localhost:9000      |\n| API Docs    | http://localhost:9000/docs |\n\n---\n\n## API Endpoints\n\n### Health\n\n```\nGET /health\n```\n\n```json\n{ \"status\": \"ok\", \"chromadb\": \"embedded\", \"collections\": 1 }\n```\n\n---\n\n### Insert / Update — Single Document\n\n```\nPOST /documents/upsert\n```\n\n```json\n{\n  \"collection\": \"default\",\n  \"id\": \"doc_1\",\n  \"text\": \"Machine learning is a subset of artificial intelligence\",\n  \"metadata\": { \"source\": \"wiki\", \"topic\": \"ai\" }\n}\n```\n\n```json\n{\n  \"status\": \"ok\",\n  \"message\": \"Upserted document 'doc_1' into 'default'. Total: 1\"\n}\n```\n\n---\n\n### Insert / Update — Bulk Documents\n\n```\nPOST /documents/upsert/bulk\n```\n\n```json\n{\n  \"collection\": \"default\",\n  \"documents\": [\n    {\n      \"id\": \"doc_1\",\n      \"text\": \"Machine learning is a subset of AI\",\n      \"metadata\": { \"source\": \"wiki\" }\n    },\n    {\n      \"id\": \"doc_2\",\n      \"text\": \"FastAPI is a modern Python web framework\",\n      \"metadata\": { \"source\": \"blog\" }\n    }\n  ]\n}\n```\n\n```json\n{ \"status\": \"ok\", \"message\": \"Upserted 2 documents into 'default'. Total: 2\" }\n```\n\n\u003e Both endpoints behave as **upsert** — if the ID exists it gets updated, if not it gets inserted.\n\n---\n\n### Delete Documents\n\n```\nDELETE /documents/delete\n```\n\n```json\n{\n  \"collection\": \"default\",\n  \"ids\": [\"doc_1\", \"doc_2\"]\n}\n```\n\n```json\n{\n  \"status\": \"ok\",\n  \"message\": \"Deleted 2 documents from 'default'. Remaining: 0\"\n}\n```\n\n---\n\n### Semantic Search\n\n```\nPOST /search\n```\n\n```json\n{\n  \"query\": \"what is artificial intelligence?\",\n  \"top_k\": 3,\n  \"collection\": \"default\",\n  \"where\": { \"source\": \"wiki\" }\n}\n```\n\n```json\n{\n  \"query\": \"what is artificial intelligence?\",\n  \"total\": 1,\n  \"results\": [\n    {\n      \"id\": \"doc_1\",\n      \"text\": \"Machine learning is a subset of artificial intelligence\",\n      \"metadata\": { \"source\": \"wiki\", \"topic\": \"ai\" },\n      \"distance\": 0.082\n    }\n  ]\n}\n```\n\n\u003e `where` is optional — use it to filter results by metadata fields.\n\n---\n\n### Collections\n\n```\nGET    /collections           # list all collections with document counts\nGET    /collections/{name}    # get a specific collection\nDELETE /collections/{name}    # drop an entire collection\n```\n\n---\n\n## Environment Variables\n\n| Variable             | Default                  | Description                       |\n| -------------------- | ------------------------ | --------------------------------- |\n| `CHROMA_PERSIST_DIR` | `/app/chromadb`          | Path where ChromaDB persists data |\n| `EMBEDDING_MODEL`    | `BAAI/bge-small-en-v1.5` | SentenceTransformer model name    |\n| `DEFAULT_COLLECTION` | `default`                | Default collection name           |\n\n---\n\n## Capacity Guide\n\nUsing the default `BAAI/bge-small-en-v1.5` model (384 dimensions):\n\n| Records | RAM Required |\n| ------- | ------------ |\n| 100K    | ~1 GB        |\n| 500K    | ~4 GB        |\n| 1M      | ~8 GB        |\n| 2M      | ~16 GB       |\n\n\u003e Recommended for up to **2 million records**. For larger scale, switch to a dedicated ChromaDB or Qdrant server.\n\n---\n\n## Docker Compose\n\n```yaml\nservices:\n  searchkit:\n    image: surinderlohat/searchkit:latest\n    container_name: searchkit\n    restart: unless-stopped\n    ports:\n      - \"9000:9000\"\n    environment:\n      - CHROMA_PERSIST_DIR=/app/chromadb\n      - EMBEDDING_MODEL=BAAI/bge-small-en-v1.5\n      - DEFAULT_COLLECTION=default\n      - SENTENCE_TRANSFORMERS_HOME=/app/models\n      - EMBEDDING_DEVICE=auto\n      - LOG_LEVEL=INFO # INFO | DEBUG | WARNING | ERROR\n      - LOG_FORMAT=text # text | json (use json in prod)\n      - MEMORY_WARN_MB=3500 # log warning when RAM exceeds this\n      - MEMORY_LIMIT_MB=4000 # refuse writes above this\n    volumes:\n      - ./data/chromadb:/app/chromadb # vector DB — directly on host disk\n      - ./data/models:/app/models # model cache — directly on host disk\n```\n\n---\n\n## How Your Main Service Uses This\n\n\u003e **Inside Docker** use `http://searchkit:9000` — Docker resolves the container name automatically.\n\u003e **From your host machine** (local dev, Postman, curl) use `http://localhost:9000`.\n\n```python\nimport httpx\n\n# Use \"searchkit\" as host when running inside Docker Compose\n# Use \"localhost\" when calling from your host machine\nSEARCHKIT_URL = \"http://searchkit:9000\"\n\nasync def insert_document(id: str, text: str, metadata: dict):\n    async with httpx.AsyncClient() as client:\n        await client.post(f\"{SEARCHKIT_URL}/documents/upsert\", json={\n            \"collection\": \"default\",\n            \"id\": id,\n            \"text\": text,\n            \"metadata\": metadata,\n        })\n\nasync def search(query: str) -\u003e list:\n    async with httpx.AsyncClient() as client:\n        res = await client.post(f\"{SEARCHKIT_URL}/search\", json={\n            \"query\": query,\n            \"top_k\": 5,\n            \"collection\": \"default\",\n        })\n        return res.json()[\"results\"]\n```\n\nYour main service never touches ChromaDB directly — it just calls HTTP endpoints.\n\n---\n\n## Running Locally (Without Docker)\n\nUseful for development and testing before building the Docker image.\n\n### Prerequisites\n\n- Python 3.11+\n- pip\n\n### 1. Create Virtual Environment\n\n```bash\ncd chroma-service-final\npython -m venv .venv\n\n# Linux / Mac\nsource .venv/bin/activate\n\n# Windows\n.venv\\Scripts\\activate\n```\n\n### 2. Install Dependencies\n\nInstall CPU-only torch **first** — otherwise pip pulls the CUDA version (~2.5 GB):\n\n```bash\npip install torch==2.3.0+cpu torchvision==0.18.0+cpu \\\n    --extra-index-url https://download.pytorch.org/whl/cpu\n\npip install -r requirements.txt\n```\n\n### 3. Create Local Config\n\n```bash\ncat \u003e .env.local \u003c\u003c 'ENVEOF'\nCHROMA_PERSIST_DIR=./data/chromadb\nEMBEDDING_MODEL=BAAI/bge-small-en-v1.5\nSENTENCE_TRANSFORMERS_HOME=./data/models\nEMBEDDING_DEVICE=auto\nDEFAULT_COLLECTION=default\nLOG_LEVEL=DEBUG\nLOG_FORMAT=text\nMEMORY_WARN_MB=3500\nMEMORY_LIMIT_MB=4000\nENVEOF\n```\n\n\u003e `.env.local` is already in `.gitignore` — safe to put local values here.\n\n### 4. Run\n\n```bash\nexport $(cat .env.local | xargs) \u0026\u0026 uvicorn app.main:app --reload --port 9000\n```\n\n`--reload` automatically restarts the server on every code change.\n\n### 5. Test\n\n| URL                          | Description                                   |\n| ---------------------------- | --------------------------------------------- |\n| http://localhost:9000/docs   | Swagger UI — test all endpoints interactively |\n| http://localhost:9000/health | Quick sanity check                            |\n\n### Switching Back to Docker\n\n```bash\n# Deactivate venv\ndeactivate\n\n# Build and run with Docker\ndocker compose up -d --build\n```\n\n\u003e Local `./data/` folder is shared between local and Docker runs — no data loss when switching.\n\n---\n\n## Make Commands\n\nAll common tasks are available via `make`. Requires `make` to be installed.\n\n```bash\n# Development\nmake dev          # run locally with hot reload\nmake run          # run locally without hot reload\n\n# Code Quality\nmake format       # format code with ruff\nmake lint         # check lint issues\nmake lint-fix     # auto fix lint issues\nmake check        # format + lint before committing\n\n# Docker\nmake docker-up    # start docker\nmake docker-build # build and start docker\nmake docker-down  # stop docker\nmake docker-logs  # tail container logs\n```\n\n\u003e Run `make check` before every commit to catch formatting and lint issues early.\n\n---\n\n## Acknowledgements\n\nThis project is built on the shoulders of some fantastic open source tools and models. Huge thanks to the teams and communities behind them:\n\n| Tool                                                                       | What it does in this project                                                       |\n| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |\n| 🤗 [BAAI/bge-small-en-v1.5](https://huggingface.co/BAAI/bge-small-en-v1.5) | The embedding model that powers semantic search — fast, accurate, and CPU-friendly |\n| 🟣 [ChromaDB](https://www.trychroma.com)                                   | The embedded vector store that handles storage, indexing, and similarity search    |\n| ⚡ [FastAPI](https://fastapi.tiangolo.com)                                 | The web framework powering the REST API and auto-generated Swagger docs            |\n| 🐳 [Docker](https://www.docker.com)                                        | Containerisation that makes the whole service portable and production-ready        |\n| 🐙 [GitHub Actions](https://github.com/features/actions)                   | CI/CD pipeline for automated linting, building, and publishing to Docker Hub       |\n| 🤗 [Sentence Transformers](https://www.sbert.net)                          | Python library that makes embedding generation simple and model-agnostic           |\n| 🔥 [PyTorch](https://pytorch.org)                                          | The deep learning backbone that runs the embedding model                           |\n| 🦄 [Uvicorn](https://www.uvicorn.org)                                      | The lightning-fast ASGI server that runs FastAPI in production                     |\n| ✅ [Pydantic](https://docs.pydantic.dev)                                   | Data validation and serialisation for all request and response models              |\n| 🐍 [psutil](https://github.com/giampaolo/psutil)                           | System memory monitoring to keep the service within safe RAM limits                |\n| 🧹 [Ruff](https://docs.astral.sh/ruff)                                     | Blazing-fast Python linter and formatter that keeps the codebase clean             |\n\n---\n\n## Author \u0026 License\n\nCreated by **Surinder Singh** — [github.com/surinderlohat](https://github.com/surinderlohat)\n\nLicensed under the [MIT License](./LICENSE).\n© 2026 Surinder Singh. All rights reserved.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsurinderlohat%2Fsearchkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsurinderlohat%2Fsearchkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsurinderlohat%2Fsearchkit/lists"}