# Product Profile Ingestion Data ingestion pipeline for an agricultural treatment assistant. It reads local product profile JSON files and label-chunk JSONL files, then loads them into the configured backends: - **Weaviate** — stores the semantic `retrieval_summary` from product profiles in the `ProductProfile` collection for vector search. - **Microsoft SQL Server** — stores structured product metadata in `products`, per-crop use rules in `product_uses`, and label text chunks in `label_chunks`. The pipeline is **idempotent**: a hash manifest (`.processed_manifest.json`) tracks which files have already been ingested, so re-running skips unchanged files unless `--force` is used. ## Prerequisites - Python 3.10+ - [Docker](https://www.docker.com/) (for local Weaviate) - Microsoft SQL Server with the `products`, `product_uses`, and `label_chunks` tables already created - [ODBC Driver for SQL Server](https://learn.microsoft.com/en-us/sql/connect/odbc/download-odbc-driver-for-sql-server) (e.g. ODBC Driver 17 or 18) ## Setup 1. **Clone the repository** and enter the project directory. 2. **Create a virtual environment** and install dependencies: ```bash python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate pip install -r requirements.txt ``` 3. **Configure environment variables**: ```bash cp .env.example .env ``` Edit `.env` with your Gemini API key, Weaviate connection settings, and SQL Server credentials. 4. **Start Weaviate** (uses the Gemini `text2vec-google` vectorizer; requires v1.32+ for `taskType` support): ```bash docker compose up -d ``` 5. **Create the Weaviate collection** (or let `ingest.py` create it on first run): ```bash python setup_weaviate.py ``` The `ProductProfile` collection is configured to embed `retrieval_summary` with `gemini-embedding-001` and the `RETRIEVAL_DOCUMENT` task type. If you change embedding settings on an existing database, recreate the collection: ```bash python setup_weaviate.py --recreate python ingest.py --force ``` ## Usage Place product profile JSON files under `data/productProfiles/` and label-chunk JSONL files under `data/chunks/`, then run: ```bash # Process new or changed profile and chunk files python ingest.py # Scan different directories python ingest.py --profiles-dir path/to/profiles python ingest.py --chunks-dir path/to/chunks # Process a single file python ingest.py --file data/productProfiles/ramin_sc_0916.json python ingest.py --chunk-file data/chunks/delan_pro_16562.jsonl # Reprocess everything, ignoring the manifest python ingest.py --force ``` Profiles are processed before chunks in each run. Chunk rows reference `product_id` as a foreign key to `products`, so the matching product profile should be ingested first (the default run order handles this when both files are present). Exit code `0` means all targeted files succeeded; `1` means at least one file failed (partial SQL transactions are rolled back per file). ## Configuration | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `GEMINI_API_KEY` | Yes | — | API key for Weaviate's Gemini vectorizer | | `WEAVIATE_HOST` | No | `localhost` | Weaviate HTTP host | | `WEAVIATE_HTTP_PORT` | No | `8080` | Weaviate HTTP port | | `WEAVIATE_GRPC_PORT` | No | `50051` | Weaviate gRPC port | | `SQL_DRIVER` | Yes | — | ODBC driver name (e.g. `ODBC Driver 17 for SQL Server`) | | `SQL_SERVER` | Yes | — | SQL Server hostname | | `SQL_DATABASE` | Yes | — | Database name | | `SQL_USERNAME` | Yes | — | SQL login | | `SQL_PASSWORD` | Yes | — | SQL password | ## Product profile JSON format Each file must include at least `product_id`, `product_name`, and `retrieval_summary`. See `data/productProfiles/ramin_sc_0916.json` for a full example with metadata arrays and `uses` rules. **Weaviate** receives: - `product_id`, `product_name`, `retrieval_summary` **SQL `products`** receives global metadata (registration, manufacturer, action flags, spray volumes, JSON-encoded arrays for ingredients, crops, diseases, etc.). **SQL `product_uses`** receives one row per entry in the `uses` array (crop, disease, dose ranges, treatment intervals, growth stages, and related fields). ## Label chunk JSONL format Each `.jsonl` file contains one JSON object per line. See `data/chunks/delan_pro_16562.jsonl` for an example. **SQL `label_chunks`** receives one row per line with: | JSON field | SQL column | Notes | |------------|------------|-------| | `chunk_id` | `chunk_id` | Primary key | | `product_id` | `product_id` | Foreign key to `products` | | `product_name` | `product_name` | | | `target_crops` | `target_crops` | Stored as JSON string via `json.dumps()` | | `target_diseases` | `target_diseases` | Stored as JSON string via `json.dumps()` | | `chunk_type` | `chunk_type` | | | `chunk_text` | `chunk_text` | | On re-ingestion, all existing `label_chunks` rows for the file's `product_id` are deleted and replaced with the fresh set from the file. ## Project layout ``` product_profile_ingestion/ ├── data/ │ ├── productProfiles/ # Product profile JSON files (*.json) │ └── chunks/ # Label-chunk JSONL files (*.jsonl) ├── ingest.py # Main ingestion script ├── setup_weaviate.py # ProductProfile collection schema (Gemini embeddings) ├── requirements.txt # Python dependencies ├── docker-compose.yml # Local Weaviate instance ├── .env.example # Environment variable template └── .processed_manifest.json # Generated at runtime (gitignored) ``` ## Dependencies - [weaviate-client](https://weaviate.io/developers/weaviate/client-libraries/python) — Weaviate Python client - [pyodbc](https://github.com/mkleehammer/pyodbc) — SQL Server connectivity - [python-dotenv](https://github.com/theskumar/python-dotenv) — Load `.env` configuration