AI-powered product catalog management for Indian retail
Extract structured data from text & images · Auto-assign HSN codes · Export to ONDC
Kirana stores and small retailers often manage inventory with handwritten notes, phone photos, and word-of-mouth. KatalogAI bridges that gap — give it a product label photo or a plain-text description, and it returns a fully structured catalog entry with the correct HSN code, confidence scores, and ONDC-ready output.
- Features
- How It Works
- Architecture
- Tech Stack
- Quick Start
- API Usage
- API Reference
- Development
- Contributing
| Multi-modal ingestion | Free-form text or product images (JPG/PNG/WEBP) |
| AI-powered OCR | PaddleOCR + Google Gemini 2.0 Flash for accurate label reading |
| Semantic HSN search | pgvector embeddings match products to the right HSN code automatically |
| Confidence scoring | Per-field scores with weighted aggregation — you know what to trust |
| Human-in-the-loop | Review queue surfaces low-confidence fields for manual correction |
| ONDC-ready output | Native catalog item format for Open Network for Digital Commerce |
| Multi-tenant | API key isolation with per-tenant rate limiting |
| Async by default | FastAPI + async SQLAlchemy + ARQ background workers |
Input (text or image)
│
▼
┌───────────────────┐
│ Text Parser │ Regex + spaCy extracts name, brand, weight, MRP
│ or OCR + VLM │ PaddleOCR reads labels; Gemini structures the result
└────────┬──────────┘
│
▼
┌───────────────────┐
│ HSN Search │ Sentence-Transformers embed the product description
│ │ pgvector finds the closest HSN code match
└────────┬──────────┘
│
▼
┌───────────────────┐
│ Confidence Score │ Each field gets a score; low-confidence fields
│ │ enter the human review queue
└────────┬──────────┘
│
▼
ONDC Catalog Item ✓
Input
Parle-G biscuits 200g, MRP Rs 30
Output
{
"id": "prod_01j...",
"name": "Parle-G",
"brand": "Parle",
"category": "biscuits",
"weight": "200g",
"mrp": 30.0,
"currency": "INR",
"hsn_code": "1905",
"confidence": {
"overall": 0.91,
"fields": {
"name": 0.98, "brand": 0.97, "mrp": 0.99,
"weight": 0.95, "hsn_code": 0.72
}
},
"ondc": { ... }
}┌─────────────────────────────────────────────────────────┐
│ KatalogAI │
│ │
│ REST API (FastAPI) │
│ ┌──────────────┬──────────────┬───────────────────┐ │
│ │ /ingest │ /products │ /review │ │
│ │ text/image │ CRUD │ human-in-the-loop│ │
│ └──────┬───────┴──────────────┴───────────────────┘ │
│ │ │
│ ┌──────▼──────────────────────────────────────────┐ │
│ │ Extraction Pipeline │ │
│ │ Regex/spaCy → Gemini VLM → HSN Search │ │
│ │ (pgvector) │ │
│ └──────────────────────────┬──────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼──────────────────────┐ │
│ │ Background Workers (ARQ) │ │
│ │ Image processing jobs · Async HSN lookups │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌────▼──────┐
│ PostgreSQL│ │ Redis │ │ pgvector │
│ (primary) │ │ (queue & │ │ (HSN embeds│
│ │ │ cache) │ │ + search) │
└───────────┘ └───────────┘ └────────────┘
| Layer | Technology |
|---|---|
| API Framework | FastAPI 0.115+ |
| Language | Python 3.12+ |
| Database | PostgreSQL 16 · async SQLAlchemy 2.0 · Alembic |
| Vector Search | pgvector · Sentence-Transformers |
| Cache & Queue | Redis 7 · ARQ |
| AI / Vision | Google Gemini 2.0 Flash · PaddleOCR · spaCy |
| Auth | API keys · bcrypt |
| Observability | Sentry · Prometheus |
| Packaging | uv · pyproject.toml |
- Python 3.12+
- PostgreSQL 16 with the
pgvectorextension - Redis 7+
- Google Gemini API key
git clone https://github.com/Karan-Raj-KR/KatalogAI.git
cd KatalogAI
cp .env.example .env # fill in your keys
docker-compose up -dThe API will be live at http://localhost:8000. Interactive docs at http://localhost:8000/docs.
git clone https://github.com/Karan-Raj-KR/KatalogAI.git
cd KatalogAI
# Install (uv recommended, pip also works)
pip install -e ".[dev]"
# Configure
cp .env.example .env
# Run migrations & seed HSN codes
alembic upgrade head
python -m app.db.init_db
# Start the server
uvicorn app.main:app --reload| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL DSN (postgresql+asyncpg://...) |
REDIS_URL |
Yes | Redis DSN (redis://localhost:6379) |
GEMINI_API_KEY |
Yes | Google AI Studio key |
SECRET_KEY |
Yes | Random secret for API key signing |
SENTRY_DSN |
No | Sentry project DSN for error tracking |
curl -X POST http://localhost:8000/api/v1/keys \
-H "Content-Type: application/json" \
-d '{"name": "my-app", "rate_limit_per_min": 60}'{ "key": "kat_live_abc123...", "name": "my-app" }curl -X POST http://localhost:8000/api/v1/ingest/text \
-H "X-API-Key: kat_live_abc123..." \
-H "Content-Type: application/json" \
-d '{"text": "Parle-G biscuits 200g, MRP Rs 30"}'# Upload — returns a job ID
curl -X POST http://localhost:8000/api/v1/ingest/image \
-H "X-API-Key: kat_live_abc123..." \
-F "file=@product_label.jpg"
# Poll for completion
curl http://localhost:8000/api/v1/jobs/{job_id} \
-H "X-API-Key: kat_live_abc123..."
# Fetch the product once the job is done
curl http://localhost:8000/api/v1/jobs/{job_id}/product \
-H "X-API-Key: kat_live_abc123..."# Paginated list
curl "http://localhost:8000/api/v1/products?page=1&limit=20" \
-H "X-API-Key: kat_live_abc123..."
# Update a field
curl -X PATCH http://localhost:8000/api/v1/products/{id} \
-H "X-API-Key: kat_live_abc123..." \
-H "Content-Type: application/json" \
-d '{"mrp": 35.0}'# List items pending human review
curl http://localhost:8000/api/v1/review \
-H "X-API-Key: kat_live_abc123..."
# Accept or correct a flagged field
curl -X POST http://localhost:8000/api/v1/review/{id}/resolve \
-H "X-API-Key: kat_live_abc123..." \
-H "Content-Type: application/json" \
-d '{"value": "1905", "action": "accept"}'| Endpoint | Method | Description |
|---|---|---|
/api/v1/ingest/text |
POST | Ingest product from free-form text |
/api/v1/ingest/image |
POST | Upload image for async processing |
/api/v1/products |
GET | List products (paginated) |
/api/v1/products/{id} |
GET | Get a single product |
/api/v1/products/{id} |
PATCH | Update product fields |
/api/v1/jobs/{id} |
GET | Get background job status |
/api/v1/jobs/{id}/product |
GET | Fetch product from a completed job |
/api/v1/review |
GET | List pending review items |
/api/v1/review/{id}/resolve |
POST | Resolve a review item |
/api/v1/keys |
POST | Create an API key |
/health |
GET | Health check |
Full interactive docs: http://localhost:8000/docs
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
make test
# Lint
make lint
# Type-check
make typecheckkatalogai/
├── app/
│ ├── api/v1/ # Route handlers
│ │ ├── ingest.py # Text & image ingestion endpoints
│ │ ├── products.py # Product CRUD
│ │ ├── review.py # Review workflow
│ │ └── keys.py # API key management
│ ├── ml/ # ML components
│ │ ├── text_parser.py # Regex + spaCy extraction
│ │ ├── vlm.py # Gemini VLM integration
│ │ ├── ocr.py # PaddleOCR wrapper
│ │ ├── confidence.py # Per-field confidence scoring
│ │ ├── prompts/ # Gemini prompt templates
│ │ └── hsn/ # HSN code search & verification
│ ├── models/ # SQLAlchemy ORM models
│ ├── schemas/ # Pydantic request/response schemas
│ ├── services/ # Business logic (no FastAPI imports)
│ ├── workers/ # ARQ background tasks
│ ├── db/ # DB setup, sessions, init scripts
│ ├── core/ # Security, exceptions, config
│ └── utils/ # Shared utilities
├── tests/ # Unit & integration tests
├── alembic/ # Database migrations
├── docker/ # Docker & compose config
├── docs/ # Additional documentation
├── scripts/ # Utility scripts
├── alembic.ini
└── pyproject.toml
Contributions are welcome! Please read the Contributing Guide and Code of Conduct before opening a PR.
Quick contribution flow:
- Fork the repo and create a feature branch
- Make your changes with tests
- Run
make lint && make test - Open a pull request
MIT — see LICENSE for details.
- Issues / Bugs: github.com/Karan-Raj-KR/KatalogAI/issues
- Discussions: github.com/Karan-Raj-KR/KatalogAI/discussions
Built with ONDC protocol specs · Google Gemini · PaddleOCR · pgvector