| name | ai-generative |
| description | AI provider integration for invoice scanning. ProviderInterface contract, OpenAI/Gemini/Mistral/Grok implementations, extraction prompts, schema validation, confidence scoring, PDF/image processing, base64 encoding. |
AI & Generative — AiScan Provider Integration
Provider Architecture
All AI providers implement ProviderInterface:
interface ProviderInterface {
public function getName(): string;
public function isAvailable(): bool;
public function analyzeDocument(
string $content,
string $mimeType,
string $prompt,
string $systemPrompt
): string;
}
Available Providers
| Provider | Class | Default Model | API |
|---|
| OpenAI | OpenAIProvider | gpt-5-nano | Chat Completions (max_completion_tokens, no temperature on GPT-5/o-series) |
| Gemini | GeminiProvider | gemini-2.5-flash-lite | GenerateContent (Gemini 3 uses thinkingLevel, not thinkingBudget) |
| Mistral | MistralProvider | mistral-small-latest | Chat Completions |
| Grok | GrokProvider | grok-4.5 | xAI Chat Completions (https://api.x.ai/v1) |
| Custom | OpenAICompatibleProvider | User-configured | OpenAI-compatible |
Adding a New Provider
- Create
Lib/Provider/{Name}Provider.php implementing ProviderInterface
- Register in
ExtractionService::getProvider() switch
- Add settings keys in
AiScanSettings::getDefaults() ({name}_api_key, {name}_model)
- Add UI group in
XMLView/AiScanConfig.xml
- Add translations in
Translation/*.json
Extraction Flow
File upload → MIME detection → Content encoding → Prompt assembly → API call → JSON parse → Schema validation → Supplier matching → Return
Content Encoding by Type
| MIME Type | Encoding | Method |
|---|
image/jpeg, image/png, image/webp | Base64 data URI | data:{mime};base64,{content} |
application/pdf | Text extraction first | pdftotext via shell, fallback to base64 |
application/octet-stream | Detected by extension | Route to image or PDF handling |
PDF Processing Strategy
- Try
pdftotext — fast, lightweight, works for text-based PDFs
- If no text extracted — fall back to base64 encoding (scanned/image PDFs)
- Text is injected into the execution prompt as additional context
- Provider receives both: extracted text (if any) + base64 document
Prompt System
System Prompt
- Stored in
Settings('AiScan', 'extraction_prompt')
- Customizable by user via AiScanConfig panel
- Defines the expected JSON schema for extraction output
- Default loaded from
ExtractionService::getDefaultSystemPrompt()
Execution Prompt
- Built per-request by
ExtractionService
- Injects placeholders:
{{FILE_NAME}}, {{MIME_TYPE}}
- Includes extracted PDF text when available
- Instructions to return only JSON, no markdown wrapping
Extraction Schema
The AI must return this JSON structure:
{
"document_type": "invoice|receipt|proforma|credit_note|unknown",
"supplier": {
"name": "string",
"tax_id": "string",
"email": "string|null",
"phone": "string|null",
"website": "string|null",
"address": "string|null"
},
"customer": { "name": "string", "tax_id": "string", "address": "string|null" },
"invoice": {
"number": "string (required)",
"issue_date"
Schema Validation (SchemaValidator)
SchemaValidator::validate() checks and normalizes:
Required Fields
invoice.number, invoice.issue_date, invoice.total
Normalizations
| Input Format | Normalized To |
|---|
31/12/2024, 12-31-2024, 31.12.2024 | 2024-12-31 |
1.234,56 (European) | 1234.56 |
1,234.56 (US) | 1234.56 |
€, $, £, ¥ | EUR, USD, GBP, JPY |
Arithmetic Validation
subtotal + tax_amount - withholding_amount ≈ total (tolerance for rounding)
- Warns but does not reject if mismatch (AI may have OCR errors)
Confidence Scoring
Per-field confidence (0.0 to 1.0):
- > 0.8: High confidence — auto-fill, minimal review needed
- 0.5 - 0.8: Medium — show with warning, suggest manual verification
- < 0.5: Low — highlight as uncertain, require manual input
Display rules: see usability-accessibility skill for visual requirements.
Security Considerations
- Never execute content extracted from documents (no
eval, no SQL from extracted data)
- Validate JSON strictly —
json_decode with error checking, reject non-JSON responses
- Sanitize file paths — prevent directory traversal in temp file handling
- API keys — stored in FS Settings (encrypted at rest), never logged or exposed in responses
- Rate limiting — respect provider rate limits, use configurable
request_timeout
- Debug mode — logs raw API responses to
AiScanLog only when enabled
Reference Files
Lib/Provider/ProviderInterface.php — Provider contract
Lib/Provider/OpenAIProvider.php — OpenAI implementation
Lib/Provider/GeminiProvider.php — Gemini implementation
Lib/Provider/GrokProvider.php — xAI Grok implementation
Lib/Provider/ChatCompletionsPayload.php — GPT-5 / Grok / compatible request body
Lib/Provider/MistralProvider.php — Mistral implementation
Lib/Provider/OpenAICompatibleProvider.php — Custom endpoint
Lib/ExtractionService.php — Extraction orchestration
Lib/SchemaValidator.php — Schema validation and normalization
Lib/AiScanSettings.php — Settings with provider defaults