Stage 16 — AI Layer: VIN decoder + diagnostic / parts / price helpers
VinDecoder (deterministic, no API): - ISO 3779/3780 parsing: WMI manufacturer (~60 brands), year (cyclical with post-2010 disambiguation via position 7), region, plant, NA checksum - Strip non-VIN chars, accept dashes/spaces, reject I/O/Q per spec AiAssistantService: - Refactored provider HTTP into postClaude/postOpenAI/postGemini so both chat history and one-shot calls share the same transport - singleShot(system, userPrompt, provider?) for fire-and-forget calls - 4 specialized helpers with tight prompts: - suggestDiagnosis(WO) — diagnostician based on complaint + VIN info - suggestParts(WO, task) — OEM parts list for an operation - suggestPrice(Part) — markup recommendation with justification - vinRecommendations(vin, mileage) — scheduled maintenance from decoded VIN - monthlyUsage() — token spend MTD by provider Filament: - VehicleResource: "Decode VIN" + "AI: recomandări" actions - WorkOrderResource Edit: "AI: sugerează diagnostic" header action - PartResource: "AI: preț recomandat" action - Shared views: filament.tenant.ai-reply, filament.tenant.vin-decode - AiAssistant page shows monthly token usage banner Tests (13 new): - 8 VinDecoder unit tests with real VIN samples (Honda 2003, VW 1999, Audi 2014, Dacia, unknown WMI, lowercase/dashes, forbidden chars) - 5 AiHelpers feature tests with Http::fake covering all providers + no-key fallback + token usage aggregation Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
@php
|
||||
$valid = $info['valid_length'] ?? false;
|
||||
@endphp
|
||||
<div class="space-y-4">
|
||||
@if (! $valid)
|
||||
<div class="p-3 rounded-md bg-yellow-50 dark:bg-yellow-900/30 border border-yellow-200 dark:border-yellow-700 text-sm text-yellow-900 dark:text-yellow-100">
|
||||
{{ $info['reason'] ?? 'VIN invalid.' }}
|
||||
</div>
|
||||
@else
|
||||
<dl class="grid grid-cols-2 gap-x-4 gap-y-2 text-sm">
|
||||
<dt class="text-gray-500 dark:text-gray-400">VIN</dt>
|
||||
<dd class="font-mono text-gray-900 dark:text-gray-100">{{ $info['vin'] }}</dd>
|
||||
|
||||
<dt class="text-gray-500 dark:text-gray-400">Regiune</dt>
|
||||
<dd class="text-gray-900 dark:text-gray-100">{{ $info['region'] ?? '—' }}</dd>
|
||||
|
||||
<dt class="text-gray-500 dark:text-gray-400">Țară</dt>
|
||||
<dd class="text-gray-900 dark:text-gray-100">{{ $info['country'] ?? '—' }}</dd>
|
||||
|
||||
<dt class="text-gray-500 dark:text-gray-400">Producător</dt>
|
||||
<dd class="text-gray-900 dark:text-gray-100 font-semibold">{{ $info['manufacturer'] ?? '— (WMI necunoscut)' }}</dd>
|
||||
|
||||
<dt class="text-gray-500 dark:text-gray-400">An model</dt>
|
||||
<dd class="text-gray-900 dark:text-gray-100 font-semibold">{{ $info['year'] ?? '—' }}</dd>
|
||||
|
||||
<dt class="text-gray-500 dark:text-gray-400">Cod uzină</dt>
|
||||
<dd class="font-mono text-gray-900 dark:text-gray-100">{{ $info['plant'] ?? '—' }}</dd>
|
||||
|
||||
<dt class="text-gray-500 dark:text-gray-400">Checksum (ISO)</dt>
|
||||
<dd class="text-gray-900 dark:text-gray-100">
|
||||
@if ($info['checksum_valid'])
|
||||
<span class="text-green-600 dark:text-green-400">✓ valid</span>
|
||||
@else
|
||||
<span class="text-gray-500">— (multe VIN-uri europene nu respectă checksum-ul NA)</span>
|
||||
@endif
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 border-t border-gray-200 dark:border-gray-700 pt-2">
|
||||
Detalii granulare (model, motorizare) necesită bază TecDoc/NHTSA. Folosește butonul „AI: recomandări" pentru sugestii bazate pe an + producător.
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
Reference in New Issue
Block a user