docker-compose.yml - Added VERTEX_AI_PROJECT, VERTEX_AI_LOCATION, GEMINI_MODEL env vars to mvp-ocr
docker-compose.staging.yml - Same env vars added
docker-compose.prod.yml - Same env vars added
Implementation Details
GeminiEngine is standalone (NOT extending OcrEngine ABC) since Gemini does semantic document understanding, not traditional OCR
Uses Vertex AI SDK with GenerativeModel and generate_content() with response_mime_type="application/json" and response_schema for guaranteed JSON structure
PDFs >20MB rejected with clear error message
Lazy initialization: model not created until first extract_maintenance() call
Authentication via same WIF credential path as Google Vision (GOOGLE_APPLICATION_CREDENTIALS)
Returns MaintenanceExtractionResult with list of MaintenanceItem dataclasses (camelCase from API mapped to snake_case Python)
Test Results
18 passed in 4.81s
Test scenarios covered:
Exception hierarchy validation
Data type construction (required-only and all fields)
PDF >20MB rejection
PDF exactly at 20MB limit passes size check
Valid PDF returns structured maintenance schedules
PDF with no maintenance content returns empty array
API runtime error wrapped as GeminiProcessingError
Invalid JSON response wrapped as GeminiProcessingError
Lazy initialization verified (model is None after construction)
Model reused on subsequent calls
Acceptance Criteria Status
GeminiEngine.extract_maintenance(pdf_bytes) returns structured JSON with maintenanceSchedule array
Each schedule item has serviceName (required), intervalMiles (nullable), intervalMonths (nullable), details (nullable)
PDFs >20MB rejected with clear error message
Authentication works via WIF in container environment (same path as Google Vision)
Configuration reads from environment variables with sensible defaults
Verdict: PASS | Next: Ready for PR / Quality Review
## Milestone: Gemini Engine Module and Configuration
**Phase**: Execution | **Agent**: Feature Agent | **Status**: PASS
### Changes Made
**New Files:**
- `ocr/app/engines/gemini_engine.py` - Standalone `GeminiEngine` class with `extract_maintenance(pdf_bytes)` method
- `ocr/tests/test_gemini_engine.py` - 18 unit tests (all passing)
**Modified Files:**
- `ocr/app/config.py` - Added `vertex_ai_project`, `vertex_ai_location`, `gemini_model` settings
- `ocr/requirements.txt` - Added `google-cloud-aiplatform>=1.40.0`
- `docker-compose.yml` - Added `VERTEX_AI_PROJECT`, `VERTEX_AI_LOCATION`, `GEMINI_MODEL` env vars to mvp-ocr
- `docker-compose.staging.yml` - Same env vars added
- `docker-compose.prod.yml` - Same env vars added
### Implementation Details
- `GeminiEngine` is standalone (NOT extending `OcrEngine` ABC) since Gemini does semantic document understanding, not traditional OCR
- Uses Vertex AI SDK with `GenerativeModel` and `generate_content()` with `response_mime_type="application/json"` and `response_schema` for guaranteed JSON structure
- PDFs >20MB rejected with clear error message
- Lazy initialization: model not created until first `extract_maintenance()` call
- Authentication via same WIF credential path as Google Vision (`GOOGLE_APPLICATION_CREDENTIALS`)
- Returns `MaintenanceExtractionResult` with list of `MaintenanceItem` dataclasses (camelCase from API mapped to snake_case Python)
### Test Results
```
18 passed in 4.81s
```
Test scenarios covered:
- Exception hierarchy validation
- Data type construction (required-only and all fields)
- PDF >20MB rejection
- PDF exactly at 20MB limit passes size check
- Valid PDF returns structured maintenance schedules
- PDF with no maintenance content returns empty array
- Nullable fields handled correctly
- Missing credential file raises GeminiUnavailableError
- Missing SDK raises GeminiUnavailableError
- API runtime error wrapped as GeminiProcessingError
- Invalid JSON response wrapped as GeminiProcessingError
- Lazy initialization verified (model is None after construction)
- Model reused on subsequent calls
### Acceptance Criteria Status
- [x] GeminiEngine.extract_maintenance(pdf_bytes) returns structured JSON with maintenanceSchedule array
- [x] Each schedule item has serviceName (required), intervalMiles (nullable), intervalMonths (nullable), details (nullable)
- [x] PDFs >20MB rejected with clear error message
- [x] Authentication works via WIF in container environment (same path as Google Vision)
- [x] Configuration reads from environment variables with sensible defaults
*Verdict*: PASS | *Next*: Ready for PR / Quality Review
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Relates to #129
Milestone 4: Gemini Engine Module and Configuration
Create standalone Gemini 2.5 Flash module in the Python OCR service for maintenance schedule extraction.
Files
ocr/app/engines/gemini_engine.py(NEW)ocr/app/config.pyocr/requirements.txtdocker-compose.ymldocker-compose.staging.ymldocker-compose.prod.ymlRequirements
GeminiEngineclass (standalone, NOT extending OcrEngine) withextract_maintenance()methodGenerativeModel("gemini-2.5-flash")withgenerate_content()response_mime_type="application/json"andresponse_schemafor guaranteed JSON structureGOOGLE_APPLICATION_CREDENTIALS)VERTEX_AI_PROJECT,VERTEX_AI_LOCATION,GEMINI_MODELgoogle-cloud-aiplatform>=1.40.0to requirements.txtAcceptance Criteria
Tests
ocr/tests/test_gemini_engine.py(NEW)Milestone: Gemini Engine Module and Configuration
Phase: Execution | Agent: Feature Agent | Status: PASS
Changes Made
New Files:
ocr/app/engines/gemini_engine.py- StandaloneGeminiEngineclass withextract_maintenance(pdf_bytes)methodocr/tests/test_gemini_engine.py- 18 unit tests (all passing)Modified Files:
ocr/app/config.py- Addedvertex_ai_project,vertex_ai_location,gemini_modelsettingsocr/requirements.txt- Addedgoogle-cloud-aiplatform>=1.40.0docker-compose.yml- AddedVERTEX_AI_PROJECT,VERTEX_AI_LOCATION,GEMINI_MODELenv vars to mvp-ocrdocker-compose.staging.yml- Same env vars addeddocker-compose.prod.yml- Same env vars addedImplementation Details
GeminiEngineis standalone (NOT extendingOcrEngineABC) since Gemini does semantic document understanding, not traditional OCRGenerativeModelandgenerate_content()withresponse_mime_type="application/json"andresponse_schemafor guaranteed JSON structureextract_maintenance()callGOOGLE_APPLICATION_CREDENTIALS)MaintenanceExtractionResultwith list ofMaintenanceItemdataclasses (camelCase from API mapped to snake_case Python)Test Results
Test scenarios covered:
Acceptance Criteria Status
Verdict: PASS | Next: Ready for PR / Quality Review