From f59042105829d54d1f0c8c190bd7cb408297bfe9 Mon Sep 17 00:00:00 2001 From: Eric Gullickson <16152721+ericgullickson@users.noreply.github.com> Date: Wed, 18 Feb 2026 21:51:38 -0600 Subject: [PATCH] chore: remove NHTSA code and update documentation (refs #227) Delete vehicles/external/nhtsa/ directory (3 files), remove VPICVariable and VPICResponse from platform models. Update all documentation to reflect Gemini VIN decode via OCR service architecture. Co-Authored-By: Claude Opus 4.6 --- backend/src/features/ocr/CLAUDE.md | 2 +- backend/src/features/platform/README.md | 6 +- .../src/features/platform/models/responses.ts | 16 -- backend/src/features/vehicles/CLAUDE.md | 2 +- backend/src/features/vehicles/README.md | 36 ++- .../src/features/vehicles/external/CLAUDE.md | 6 - .../src/features/vehicles/external/README.md | 2 +- .../features/vehicles/external/nhtsa/index.ts | 16 -- .../vehicles/external/nhtsa/nhtsa.client.ts | 235 ------------------ .../vehicles/external/nhtsa/nhtsa.types.ts | 96 ------- docs/PLATFORM-SERVICES.md | 2 +- docs/TESTING.md | 10 +- docs/USER-GUIDE.md | 4 +- ocr/CLAUDE.md | 2 +- ocr/app/CLAUDE.md | 2 +- ocr/app/engines/CLAUDE.md | 6 +- 16 files changed, 35 insertions(+), 408 deletions(-) delete mode 100644 backend/src/features/vehicles/external/nhtsa/index.ts delete mode 100644 backend/src/features/vehicles/external/nhtsa/nhtsa.client.ts delete mode 100644 backend/src/features/vehicles/external/nhtsa/nhtsa.types.ts diff --git a/backend/src/features/ocr/CLAUDE.md b/backend/src/features/ocr/CLAUDE.md index e57bce8..4ceaa93 100644 --- a/backend/src/features/ocr/CLAUDE.md +++ b/backend/src/features/ocr/CLAUDE.md @@ -37,7 +37,7 @@ Backend proxy for the Python OCR microservice. Handles authentication, tier gati | File | What | When to read | | ---- | ---- | ------------ | -| `ocr-client.ts` | HTTP client to mvp-ocr Python service (extract, extractVin, extractReceipt, submitJob, submitManualJob, getJobStatus, isHealthy) | OCR service communication, error handling | +| `ocr-client.ts` | HTTP client to mvp-ocr Python service (extract, extractVin, extractReceipt, decodeVin, submitJob, submitManualJob, getJobStatus, isHealthy) | OCR service communication, error handling | ## tests/ diff --git a/backend/src/features/platform/README.md b/backend/src/features/platform/README.md index 267de43..5c9ec55 100644 --- a/backend/src/features/platform/README.md +++ b/backend/src/features/platform/README.md @@ -117,7 +117,7 @@ platform/ When implemented, VIN decoding will use: 1. **Cache First**: Check Redis (7-day TTL for success, 1-hour for failures) 2. **PostgreSQL**: Database function for high-confidence decode -3. **vPIC Fallback**: NHTSA vPIC API with circuit breaker protection +3. **OCR Service Fallback**: Gemini VIN decode via OCR service 4. **Graceful Degradation**: Return meaningful errors when all sources fail ### Database Schema @@ -164,7 +164,7 @@ When VIN decoding is implemented: ### External APIs (Planned/Future) When VIN decoding is implemented: -- **NHTSA vPIC**: https://vpic.nhtsa.dot.gov/api (VIN decoding fallback) +- **OCR Service**: Gemini VIN decode via mvp-ocr (VIN decoding fallback) ### Database Tables - **vehicle_options** - Hierarchical vehicle data (years, makes, models, trims, engines, transmissions) @@ -269,7 +269,7 @@ npm run lint ## Future Considerations ### Planned Features -- VIN decoding endpoint with PostgreSQL + vPIC fallback +- VIN decoding endpoint with PostgreSQL + Gemini/OCR service fallback - Circuit breaker pattern for external API resilience ### Potential Enhancements diff --git a/backend/src/features/platform/models/responses.ts b/backend/src/features/platform/models/responses.ts index c2770c8..ea50117 100644 --- a/backend/src/features/platform/models/responses.ts +++ b/backend/src/features/platform/models/responses.ts @@ -61,19 +61,3 @@ export interface VINDecodeResponse { error?: string; } -/** - * vPIC API response structure (NHTSA) - */ -export interface VPICVariable { - Variable: string; - Value: string | null; - ValueId: string | null; - VariableId: number; -} - -export interface VPICResponse { - Count: number; - Message: string; - SearchCriteria: string; - Results: VPICVariable[]; -} diff --git a/backend/src/features/vehicles/CLAUDE.md b/backend/src/features/vehicles/CLAUDE.md index da8c492..75c5a3f 100644 --- a/backend/src/features/vehicles/CLAUDE.md +++ b/backend/src/features/vehicles/CLAUDE.md @@ -16,6 +16,6 @@ | `data/` | Repository, database queries | Database operations | | `docs/` | Feature-specific documentation | Vehicle design details | | `events/` | Event handlers and emitters | Cross-feature event integration | -| `external/` | External service integrations (NHTSA) | VIN decoding, third-party APIs | +| `external/` | External service integrations | VIN decoding, third-party APIs | | `migrations/` | Database schema | Schema changes | | `tests/` | Unit and integration tests | Adding or modifying tests | diff --git a/backend/src/features/vehicles/README.md b/backend/src/features/vehicles/README.md index f8ce50d..f198079 100644 --- a/backend/src/features/vehicles/README.md +++ b/backend/src/features/vehicles/README.md @@ -13,7 +13,7 @@ Primary entity for vehicle management consuming MVP Platform Vehicles Service. H - `DELETE /api/vehicles/:id` - Soft delete vehicle ### VIN Decoding (Pro/Enterprise Only) -- `POST /api/vehicles/decode-vin` - Decode VIN using NHTSA vPIC API +- `POST /api/vehicles/decode-vin` - Decode VIN using Gemini via OCR service ### Hierarchical Vehicle Dropdowns **Status**: Vehicles service now proxies the platform vehicle catalog to provide fully dynamic dropdowns. Each selection step filters the next list, ensuring only valid combinations are shown. @@ -104,11 +104,7 @@ vehicles/ ├── data/ # Database layer │ └── vehicles.repository.ts ├── external/ # External service integrations -│ ├── CLAUDE.md # Integration pattern docs -│ └── nhtsa/ # NHTSA vPIC API client -│ ├── nhtsa.client.ts -│ ├── nhtsa.types.ts -│ └── index.ts +│ └── CLAUDE.md # Integration pattern docs ├── migrations/ # Feature schema │ └── 001_create_vehicles_tables.sql ├── tests/ # All tests @@ -121,14 +117,14 @@ vehicles/ ## Key Features -### 🔍 VIN Decoding (NHTSA vPIC API) +### VIN Decoding (Gemini via OCR Service) - **Tier Gating**: Pro and Enterprise users only (`vehicle.vinDecode` feature key) -- **NHTSA API**: Calls official NHTSA vPIC API for authoritative vehicle data +- **Gemini**: Calls OCR service Gemini VIN decode for authoritative vehicle data - **Caching**: Results cached in `vin_cache` table (1-year TTL, VIN data is static) - **Validation**: 17-character VIN format, excludes I/O/Q characters - **Matching**: Case-insensitive exact match against dropdown options - **Confidence Levels**: High (exact match), Medium (normalized match), None (hint only) -- **Timeout**: 5-second timeout for NHTSA API calls +- **Timeout**: 5-second timeout for OCR service calls #### Decode VIN Request ```json @@ -140,15 +136,15 @@ Authorization: Bearer Response (200): { - "year": { "value": 2021, "nhtsaValue": "2021", "confidence": "high" }, - "make": { "value": "Honda", "nhtsaValue": "HONDA", "confidence": "high" }, - "model": { "value": "Civic", "nhtsaValue": "Civic", "confidence": "high" }, - "trimLevel": { "value": "EX", "nhtsaValue": "EX", "confidence": "high" }, - "engine": { "value": null, "nhtsaValue": "2.0L L4 DOHC 16V", "confidence": "none" }, - "transmission": { "value": null, "nhtsaValue": "CVT", "confidence": "none" }, - "bodyType": { "value": null, "nhtsaValue": "Sedan", "confidence": "none" }, - "driveType": { "value": null, "nhtsaValue": "FWD", "confidence": "none" }, - "fuelType": { "value": null, "nhtsaValue": "Gasoline", "confidence": "none" } + "year": { "value": 2021, "decodedValue": "2021", "confidence": "high" }, + "make": { "value": "Honda", "decodedValue": "HONDA", "confidence": "high" }, + "model": { "value": "Civic", "decodedValue": "Civic", "confidence": "high" }, + "trimLevel": { "value": "EX", "decodedValue": "EX", "confidence": "high" }, + "engine": { "value": null, "decodedValue": "2.0L L4 DOHC 16V", "confidence": "none" }, + "transmission": { "value": null, "decodedValue": "CVT", "confidence": "none" }, + "bodyType": { "value": null, "decodedValue": "Sedan", "confidence": "none" }, + "driveType": { "value": null, "decodedValue": "FWD", "confidence": "none" }, + "fuelType": { "value": null, "decodedValue": "Gasoline", "confidence": "none" } } Error (400 - Invalid VIN): @@ -157,7 +153,7 @@ Error (400 - Invalid VIN): Error (403 - Tier Required): { "error": "TIER_REQUIRED", "requiredTier": "pro", "currentTier": "free", ... } -Error (502 - NHTSA Failure): +Error (502 - OCR Service Failure): { "error": "VIN_DECODE_FAILED", "message": "Unable to decode VIN from external service" } ``` @@ -230,7 +226,7 @@ Error (502 - NHTSA Failure): ## Testing ### Unit Tests -- `vehicles.service.test.ts` - Business logic with mocked dependencies (VIN decode, caching, CRUD operations) +- `vehicles.service.test.ts` - Business logic with mocked dependencies (VIN decode via OCR service mock, caching, CRUD operations) ### Integration Tests - `vehicles.integration.test.ts` - Complete API workflow with test database (create, read, update, delete vehicles) diff --git a/backend/src/features/vehicles/external/CLAUDE.md b/backend/src/features/vehicles/external/CLAUDE.md index fdcb3c7..bc2a3e6 100644 --- a/backend/src/features/vehicles/external/CLAUDE.md +++ b/backend/src/features/vehicles/external/CLAUDE.md @@ -5,9 +5,3 @@ | File | What | When to read | | ---- | ---- | ------------ | | `README.md` | Integration patterns, adding new services | Understanding external service conventions | - -## Subdirectories - -| Directory | What | When to read | -| --------- | ---- | ------------ | -| `nhtsa/` | NHTSA vPIC API client for VIN decoding | VIN decode feature work | diff --git a/backend/src/features/vehicles/external/README.md b/backend/src/features/vehicles/external/README.md index 0198ff9..1fc6401 100644 --- a/backend/src/features/vehicles/external/README.md +++ b/backend/src/features/vehicles/external/README.md @@ -15,7 +15,7 @@ Each integration follows this structure: ## Adding New Integrations 1. Create subdirectory: `external/{service}/` -2. Add client: `{service}.client.ts` following NHTSAClient pattern +2. Add client: `{service}.client.ts` following the axios-based client pattern 3. Add types: `{service}.types.ts` 4. Update `CLAUDE.md` with new directory 5. Add tests in `tests/unit/{service}.client.test.ts` diff --git a/backend/src/features/vehicles/external/nhtsa/index.ts b/backend/src/features/vehicles/external/nhtsa/index.ts deleted file mode 100644 index 9b2a5d4..0000000 --- a/backend/src/features/vehicles/external/nhtsa/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @ai-summary NHTSA vPIC integration exports - * @ai-context Public API for VIN decoding functionality - */ - -export { NHTSAClient } from './nhtsa.client'; -export type { - NHTSADecodeResponse, - NHTSAResult, - DecodedVehicleData, - MatchedField, - MatchConfidence, - VinCacheEntry, - DecodeVinRequest, - VinDecodeError, -} from './nhtsa.types'; diff --git a/backend/src/features/vehicles/external/nhtsa/nhtsa.client.ts b/backend/src/features/vehicles/external/nhtsa/nhtsa.client.ts deleted file mode 100644 index 1a3a8a2..0000000 --- a/backend/src/features/vehicles/external/nhtsa/nhtsa.client.ts +++ /dev/null @@ -1,235 +0,0 @@ -/** - * @ai-summary NHTSA vPIC API client for VIN decoding - * @ai-context Fetches vehicle data from NHTSA and caches results - */ - -import axios, { AxiosError } from 'axios'; -import { logger } from '../../../../core/logging/logger'; -import { NHTSADecodeResponse, VinCacheEntry } from './nhtsa.types'; -import { Pool } from 'pg'; - -/** - * VIN validation regex - * - 17 characters - * - Excludes I, O, Q (not used in VINs) - * - Alphanumeric only - */ -const VIN_REGEX = /^[A-HJ-NPR-Z0-9]{17}$/; - -/** - * Cache TTL: 1 year (VIN data is static - vehicle specs don't change) - */ -const CACHE_TTL_SECONDS = 365 * 24 * 60 * 60; - -export class NHTSAClient { - private readonly baseURL = 'https://vpic.nhtsa.dot.gov/api'; - private readonly timeout = 5000; // 5 seconds - - constructor(private readonly pool: Pool) {} - - /** - * Validate VIN format - * @throws Error if VIN format is invalid - */ - validateVin(vin: string): string { - const sanitized = vin.trim().toUpperCase(); - - if (!sanitized) { - throw new Error('VIN is required'); - } - - if (!VIN_REGEX.test(sanitized)) { - throw new Error('Invalid VIN format. VIN must be exactly 17 characters and contain only letters (except I, O, Q) and numbers.'); - } - - return sanitized; - } - - /** - * Check cache for existing VIN data - */ - async getCached(vin: string): Promise { - try { - const result = await this.pool.query<{ - vin: string; - make: string | null; - model: string | null; - year: number | null; - engine_type: string | null; - body_type: string | null; - raw_data: NHTSADecodeResponse; - cached_at: Date; - }>( - `SELECT vin, make, model, year, engine_type, body_type, raw_data, cached_at - FROM vin_cache - WHERE vin = $1 - AND cached_at > NOW() - INTERVAL '${CACHE_TTL_SECONDS} seconds'`, - [vin] - ); - - if (result.rows.length === 0) { - return null; - } - - const row = result.rows[0]; - return { - vin: row.vin, - make: row.make, - model: row.model, - year: row.year, - engineType: row.engine_type, - bodyType: row.body_type, - rawData: row.raw_data, - cachedAt: row.cached_at, - }; - } catch (error) { - logger.error('Failed to check VIN cache', { vin, error }); - return null; - } - } - - /** - * Save VIN data to cache - */ - async saveToCache(vin: string, response: NHTSADecodeResponse): Promise { - try { - const findValue = (variable: string): string | null => { - const result = response.Results.find(r => r.Variable === variable); - return result?.Value || null; - }; - - const year = findValue('Model Year'); - const make = findValue('Make'); - const model = findValue('Model'); - const engineType = findValue('Engine Model'); - const bodyType = findValue('Body Class'); - - await this.pool.query( - `INSERT INTO vin_cache (vin, make, model, year, engine_type, body_type, raw_data, cached_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, NOW()) - ON CONFLICT (vin) DO UPDATE SET - make = EXCLUDED.make, - model = EXCLUDED.model, - year = EXCLUDED.year, - engine_type = EXCLUDED.engine_type, - body_type = EXCLUDED.body_type, - raw_data = EXCLUDED.raw_data, - cached_at = NOW()`, - [vin, make, model, year ? parseInt(year) : null, engineType, bodyType, JSON.stringify(response)] - ); - - logger.debug('VIN cached', { vin }); - } catch (error) { - logger.error('Failed to cache VIN data', { vin, error }); - // Don't throw - caching failure shouldn't break the decode flow - } - } - - /** - * Decode VIN using NHTSA vPIC API - * @param vin - 17-character VIN - * @returns Raw NHTSA decode response - * @throws Error if VIN is invalid or API call fails - */ - async decodeVin(vin: string): Promise { - // Validate and sanitize VIN - const sanitizedVin = this.validateVin(vin); - - // Check cache first - const cached = await this.getCached(sanitizedVin); - if (cached) { - logger.debug('VIN cache hit', { vin: sanitizedVin }); - return cached.rawData; - } - - // Call NHTSA API - logger.info('Calling NHTSA vPIC API', { vin: sanitizedVin }); - - try { - const response = await axios.get( - `${this.baseURL}/vehicles/decodevin/${sanitizedVin}`, - { - params: { format: 'json' }, - timeout: this.timeout, - } - ); - - // Check for NHTSA-level errors - if (response.data.Count === 0) { - throw new Error('NHTSA returned no results for this VIN'); - } - - // Check for error messages in results - const errorResult = response.data.Results.find( - r => r.Variable === 'Error Code' && r.Value && r.Value !== '0' - ); - if (errorResult) { - const errorText = response.data.Results.find(r => r.Variable === 'Error Text'); - throw new Error(`NHTSA error: ${errorText?.Value || 'Unknown error'}`); - } - - // Cache the successful response - await this.saveToCache(sanitizedVin, response.data); - - return response.data; - } catch (error) { - if (axios.isAxiosError(error)) { - const axiosError = error as AxiosError; - if (axiosError.code === 'ECONNABORTED') { - logger.error('NHTSA API timeout', { vin: sanitizedVin }); - throw new Error('NHTSA API request timed out. Please try again.'); - } - if (axiosError.response) { - logger.error('NHTSA API error response', { - vin: sanitizedVin, - status: axiosError.response.status, - data: axiosError.response.data, - }); - throw new Error(`NHTSA API error: ${axiosError.response.status}`); - } - logger.error('NHTSA API network error', { vin: sanitizedVin, message: axiosError.message }); - throw new Error('Unable to connect to NHTSA API. Please try again later.'); - } - throw error; - } - } - - /** - * Extract a specific value from NHTSA response - */ - static extractValue(response: NHTSADecodeResponse, variable: string): string | null { - const result = response.Results.find(r => r.Variable === variable); - return result?.Value?.trim() || null; - } - - /** - * Extract year from NHTSA response - */ - static extractYear(response: NHTSADecodeResponse): number | null { - const value = NHTSAClient.extractValue(response, 'Model Year'); - if (!value) return null; - const parsed = parseInt(value, 10); - return isNaN(parsed) ? null : parsed; - } - - /** - * Extract engine description from NHTSA response - * Combines multiple engine-related fields - */ - static extractEngine(response: NHTSADecodeResponse): string | null { - const engineModel = NHTSAClient.extractValue(response, 'Engine Model'); - if (engineModel) return engineModel; - - // Build engine description from components - const cylinders = NHTSAClient.extractValue(response, 'Engine Number of Cylinders'); - const displacement = NHTSAClient.extractValue(response, 'Displacement (L)'); - const fuelType = NHTSAClient.extractValue(response, 'Fuel Type - Primary'); - - const parts: string[] = []; - if (cylinders) parts.push(`${cylinders}-Cylinder`); - if (displacement) parts.push(`${displacement}L`); - if (fuelType && fuelType !== 'Gasoline') parts.push(fuelType); - - return parts.length > 0 ? parts.join(' ') : null; - } -} diff --git a/backend/src/features/vehicles/external/nhtsa/nhtsa.types.ts b/backend/src/features/vehicles/external/nhtsa/nhtsa.types.ts deleted file mode 100644 index 23a18fb..0000000 --- a/backend/src/features/vehicles/external/nhtsa/nhtsa.types.ts +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @ai-summary Type definitions for NHTSA vPIC API - * @ai-context Defines request/response types for VIN decoding - */ - -/** - * Individual result from NHTSA DecodeVin API - */ -export interface NHTSAResult { - Value: string | null; - ValueId: string | null; - Variable: string; - VariableId: number; -} - -/** - * Raw response from NHTSA DecodeVin API - * GET https://vpic.nhtsa.dot.gov/api/vehicles/decodevin/{VIN}?format=json - */ -export interface NHTSADecodeResponse { - Count: number; - Message: string; - SearchCriteria: string; - Results: NHTSAResult[]; -} - -/** - * Confidence level for matched dropdown values - */ -export type MatchConfidence = 'high' | 'medium' | 'none'; - -/** - * Matched field with confidence indicator - */ -export interface MatchedField { - value: T | null; - nhtsaValue: string | null; - confidence: MatchConfidence; -} - -/** - * Decoded vehicle data with match confidence per field - * Maps NHTSA response fields to internal field names (camelCase) - * - * NHTSA Field Mappings: - * - ModelYear -> year - * - Make -> make - * - Model -> model - * - Trim -> trimLevel - * - BodyClass -> bodyType - * - DriveType -> driveType - * - FuelTypePrimary -> fuelType - * - EngineModel / EngineCylinders + EngineDisplacementL -> engine - * - TransmissionStyle -> transmission - */ -export interface DecodedVehicleData { - year: MatchedField; - make: MatchedField; - model: MatchedField; - trimLevel: MatchedField; - bodyType: MatchedField; - driveType: MatchedField; - fuelType: MatchedField; - engine: MatchedField; - transmission: MatchedField; -} - -/** - * Cached VIN data from vin_cache table - */ -export interface VinCacheEntry { - vin: string; - make: string | null; - model: string | null; - year: number | null; - engineType: string | null; - bodyType: string | null; - rawData: NHTSADecodeResponse; - cachedAt: Date; -} - -/** - * VIN decode request body - */ -export interface DecodeVinRequest { - vin: string; -} - -/** - * VIN decode error response - */ -export interface VinDecodeError { - error: 'INVALID_VIN' | 'VIN_DECODE_FAILED' | 'TIER_REQUIRED'; - message: string; - details?: string; -} diff --git a/docs/PLATFORM-SERVICES.md b/docs/PLATFORM-SERVICES.md index 8fe3686..d603bf9 100644 --- a/docs/PLATFORM-SERVICES.md +++ b/docs/PLATFORM-SERVICES.md @@ -35,7 +35,7 @@ The platform provides vehicle hierarchical data lookups: VIN decoding is planned but not yet implemented. Future capabilities will include: - `GET /api/platform/vehicle?vin={vin}` - Decode VIN to vehicle details - PostgreSQL-based VIN decode function -- NHTSA vPIC API fallback with circuit breaker +- Gemini VIN decode via OCR service - Redis caching (7-day TTL for successful decodes) **Data Source**: Vehicle data from standardized sources diff --git a/docs/TESTING.md b/docs/TESTING.md index a3a3cd0..166ca2b 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -74,7 +74,7 @@ docker compose exec mvp-frontend npm test -- --coverage Example: `vehicles.service.test.ts` - Tests VIN validation logic -- Tests vehicle creation with mocked vPIC responses +- Tests vehicle creation with mocked OCR service responses - Tests caching behavior with mocked Redis - Tests error handling paths @@ -194,7 +194,7 @@ All 15 features have test suites with unit and/or integration tests: - `vehicles` - Unit + integration tests ### Mock Strategy -- **External APIs**: Completely mocked (vPIC, Google Maps) +- **External APIs**: Completely mocked (OCR service, Google Maps) - **Database**: Real database with transactions - **Redis**: Mocked for unit tests, real for integration - **Auth**: Mocked JWT tokens for protected endpoints @@ -319,9 +319,9 @@ describe('Error Handling', () => { ).rejects.toThrow('Invalid VIN format'); }); - it('should handle vPIC API failure', async () => { - mockVpicClient.decode.mockRejectedValue(new Error('API down')); - + it('should handle OCR service failure', async () => { + mockOcrClient.decodeVin.mockRejectedValue(new Error('API down')); + const result = await vehicleService.create(validVehicle, 'user123'); expect(result.make).toBeNull(); // Graceful degradation }); diff --git a/docs/USER-GUIDE.md b/docs/USER-GUIDE.md index 50163f5..86eda4a 100644 --- a/docs/USER-GUIDE.md +++ b/docs/USER-GUIDE.md @@ -644,7 +644,7 @@ When you attempt to use a Pro feature on the Free tier, an **Upgrade Required** ### VIN Camera Scanning and Decode (Pro) -**What it does:** Use your device camera to photograph your vehicle's VIN plate, and the system automatically reads the VIN using OCR (Optical Character Recognition) and decodes it from the NHTSA database. +**What it does:** Use your device camera to photograph your vehicle's VIN plate, and the system automatically reads the VIN using OCR (Optical Character Recognition) and decodes it from the vehicle database. **How to use it:** @@ -655,7 +655,7 @@ When you attempt to use a Pro feature on the Free tier, an **Upgrade Required** 5. A **VIN OCR Review modal** appears showing the detected VIN with confidence indicators 6. Confirm or correct the VIN, then click **Accept** 7. Click the **Decode VIN** button -8. The system queries the NHTSA database and auto-populates: Year, Make, Model, Engine, Transmission, and Trim +8. The system queries the vehicle database and auto-populates: Year, Make, Model, Engine, Transmission, and Trim 9. Review the pre-filled fields and complete the remaining details This eliminates manual data entry errors and ensures accurate vehicle specifications. diff --git a/ocr/CLAUDE.md b/ocr/CLAUDE.md index e25dc65..44c3f1f 100644 --- a/ocr/CLAUDE.md +++ b/ocr/CLAUDE.md @@ -1,6 +1,6 @@ # ocr/ -Python OCR microservice. Primary engine: PaddleOCR PP-OCRv4 with optional Google Vision cloud fallback. Gemini 2.5 Flash for maintenance manual PDF extraction. Pluggable engine abstraction in `app/engines/`. +Python OCR microservice. Primary engine: PaddleOCR PP-OCRv4 with optional Google Vision cloud fallback. Gemini 2.5 Flash for maintenance manual PDF extraction and VIN decode. Pluggable engine abstraction in `app/engines/`. ## Files diff --git a/ocr/app/CLAUDE.md b/ocr/app/CLAUDE.md index a91a2be..bc0dfcc 100644 --- a/ocr/app/CLAUDE.md +++ b/ocr/app/CLAUDE.md @@ -19,7 +19,7 @@ Python OCR microservice (FastAPI). Primary engine: PaddleOCR PP-OCRv4 with optio | `models/` | Data models and schemas | Request/response types | | `patterns/` | Regex patterns and service name mapping (27 maintenance subtypes) | Pattern matching rules, service categorization | | `preprocessors/` | Image preprocessing pipeline | Image preparation before OCR | -| `routers/` | FastAPI route handlers (/extract, /extract/receipt, /extract/manual, /jobs) | API endpoint changes | +| `routers/` | FastAPI route handlers (/extract, /extract/receipt, /extract/manual, /decode, /jobs) | API endpoint changes | | `services/` | Business logic services (job queue with Redis) | Core OCR processing, async job management | | `table_extraction/` | Table detection and parsing | Structured data extraction from images | | `validators/` | Input validation | Validation rules | diff --git a/ocr/app/engines/CLAUDE.md b/ocr/app/engines/CLAUDE.md index 7df7de1..68a4e82 100644 --- a/ocr/app/engines/CLAUDE.md +++ b/ocr/app/engines/CLAUDE.md @@ -3,7 +3,7 @@ OCR engine abstraction layer. Two categories of engines: 1. **OcrEngine subclasses** (image-to-text): PaddleOCR, Google Vision, Hybrid. Accept image bytes, return text + confidence + word boxes. -2. **GeminiEngine** (PDF-to-structured-data): Standalone module for maintenance schedule extraction via Vertex AI. Accepts PDF bytes, returns structured JSON. Not an OcrEngine subclass because the interface signatures differ. +2. **GeminiEngine** (PDF-to-structured-data and VIN decode): Standalone module for maintenance schedule extraction and VIN decoding via Vertex AI. Accepts PDF bytes or VIN strings, returns structured JSON. Not an OcrEngine subclass because the interface signatures differ. ## Files @@ -15,7 +15,7 @@ OCR engine abstraction layer. Two categories of engines: | `cloud_engine.py` | Google Vision TEXT_DETECTION fallback engine (WIF authentication) | Cloud OCR configuration, API quota | | `hybrid_engine.py` | Combines primary + fallback engine with confidence threshold switching | Engine selection logic, fallback behavior | | `engine_factory.py` | Factory function and engine registry for instantiation | Adding new engine types | -| `gemini_engine.py` | Gemini 2.5 Flash integration for maintenance schedule extraction (Vertex AI SDK, 20MB PDF limit, structured JSON output) | Manual extraction debugging, Gemini configuration | +| `gemini_engine.py` | Gemini 2.5 Flash integration for maintenance schedule extraction and VIN decoding (Vertex AI SDK, 20MB PDF limit, structured JSON output) | Manual extraction debugging, VIN decode, Gemini configuration | ## Engine Selection @@ -30,4 +30,4 @@ create_engine(config) HybridEngine (tries primary, falls back if confidence < threshold) ``` -GeminiEngine is created independently by ManualExtractor, not through the engine factory. +GeminiEngine is created independently by ManualExtractor and the VIN decode router, not through the engine factory.