feat: add backend OCR client method for VIN decode (refs #225)
Add VinDecodeResponse type and OcrClient.decodeVin() method that sends JSON POST to the new /decode/vin OCR endpoint. Unlike other OCR methods, this uses JSON body instead of multipart since there is no file upload. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -131,3 +131,21 @@ export interface ManualJobResponse {
|
||||
result?: ManualExtractionResult;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/** Response from VIN decode via Gemini (OCR service) */
|
||||
export interface VinDecodeResponse {
|
||||
success: boolean;
|
||||
vin: string;
|
||||
year: number | null;
|
||||
make: string | null;
|
||||
model: string | null;
|
||||
trimLevel: string | null;
|
||||
bodyType: string | null;
|
||||
driveType: string | null;
|
||||
fuelType: string | null;
|
||||
engine: string | null;
|
||||
transmission: string | null;
|
||||
confidence: number;
|
||||
processingTimeMs: number;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
51
backend/src/features/ocr/external/ocr-client.ts
vendored
51
backend/src/features/ocr/external/ocr-client.ts
vendored
@@ -2,7 +2,7 @@
|
||||
* @ai-summary HTTP client for OCR service communication
|
||||
*/
|
||||
import { logger } from '../../../core/logging/logger';
|
||||
import type { JobResponse, ManualJobResponse, OcrResponse, ReceiptExtractionResponse, VinExtractionResponse } from '../domain/ocr.types';
|
||||
import type { JobResponse, ManualJobResponse, OcrResponse, ReceiptExtractionResponse, VinDecodeResponse, VinExtractionResponse } from '../domain/ocr.types';
|
||||
|
||||
/** OCR service configuration */
|
||||
const OCR_SERVICE_URL = process.env.OCR_SERVICE_URL || 'http://mvp-ocr:8000';
|
||||
@@ -373,6 +373,55 @@ export class OcrClient {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a VIN string into structured vehicle data via Gemini.
|
||||
*
|
||||
* Unlike other OCR methods, this sends JSON (not multipart) because
|
||||
* VIN decode has no file upload.
|
||||
*
|
||||
* @param vin - 17-character Vehicle Identification Number
|
||||
* @returns Structured vehicle data from Gemini decode
|
||||
*/
|
||||
async decodeVin(vin: string): Promise<VinDecodeResponse> {
|
||||
const url = `${this.baseUrl}/decode/vin`;
|
||||
|
||||
logger.info('OCR VIN decode request', {
|
||||
operation: 'ocr.client.decodeVin',
|
||||
url,
|
||||
vin,
|
||||
});
|
||||
|
||||
const response = await this.fetchWithTimeout(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ vin }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
logger.error('OCR VIN decode failed', {
|
||||
operation: 'ocr.client.decodeVin.error',
|
||||
status: response.status,
|
||||
error: errorText,
|
||||
});
|
||||
const err: any = new Error(`OCR service error: ${response.status} - ${errorText}`);
|
||||
err.statusCode = response.status;
|
||||
throw err;
|
||||
}
|
||||
|
||||
const result = (await response.json()) as VinDecodeResponse;
|
||||
|
||||
logger.info('OCR VIN decode completed', {
|
||||
operation: 'ocr.client.decodeVin.success',
|
||||
success: result.success,
|
||||
vin: result.vin,
|
||||
confidence: result.confidence,
|
||||
processingTimeMs: result.processingTimeMs,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OCR service is healthy.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user