Fix #55: Verbindungsfehler-Fallback – kein Retry-Loop, spezifische Fehlermeldung
This commit is contained in:
parent
54f7d9bd38
commit
1a23b3e11d
4 changed files with 78 additions and 2 deletions
|
|
@ -8,6 +8,8 @@
|
||||||
- **`LlmService`:** `probeOllama()` schreibt bei cURL-Fehlern jetzt einen Eintrag ins PHP-Fehlerlog (`docker compose logs web`)
|
- **`LlmService`:** `probeOllama()` schreibt bei cURL-Fehlern jetzt einen Eintrag ins PHP-Fehlerlog (`docker compose logs web`)
|
||||||
- **Einstellungsseite:** Neuer Button „Verbindung testen" im KI-Abschnitt; zeigt bei Erfolg verfügbare Modelle, bei Fehler cURL-Fehler, URL und Firewall-Hinweis an
|
- **Einstellungsseite:** Neuer Button „Verbindung testen" im KI-Abschnitt; zeigt bei Erfolg verfügbare Modelle, bei Fehler cURL-Fehler, URL und Firewall-Hinweis an
|
||||||
- **Route:** `POST /settings/llm-test` → `SettingsController::llmTest()`
|
- **Route:** `POST /settings/llm-test` → `SettingsController::llmTest()`
|
||||||
|
- **`BelegController`:** Bei Verbindungsfehler wird jetzt `[]` statt `null` gespeichert (verhindert endlose Auto-Retry-Schleife); Fehlermeldung differenziert zwischen „Ollama nicht erreichbar" und „kein Vorschlag generiert"
|
||||||
|
- **Beleg-Detailseite:** Zeigt bei Verbindungsfehler spezifische Meldung „Ollama nicht erreichbar" mit Link zu den Einstellungen statt der generischen Warnung
|
||||||
|
|
||||||
## 2026-06-01 – Fix: EN16931-Validierungsfehler in ZUGFeRD-Rechnungen
|
## 2026-06-01 – Fix: EN16931-Validierungsfehler in ZUGFeRD-Rechnungen
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -378,8 +378,17 @@ class BelegController extends BaseController
|
||||||
|
|
||||||
$svc = new KontierungsvorschlagService();
|
$svc = new KontierungsvorschlagService();
|
||||||
$vorschlag = $svc->generateForBeleg($beleg);
|
$vorschlag = $svc->generateForBeleg($beleg);
|
||||||
Beleg::saveKiVorschlag($id, $vorschlag);
|
// [] = "verarbeitet, kein Ergebnis"; null würde endlose Auto-Retry-Schleife auslösen
|
||||||
echo json_encode($vorschlag ?? ['error' => 'Kein Vorschlag generiert']);
|
Beleg::saveKiVorschlag($id, $vorschlag ?? []);
|
||||||
|
if ($vorschlag === null) {
|
||||||
|
$llm = new \Steuerkram\Services\LlmService();
|
||||||
|
echo json_encode($llm->isReachable()
|
||||||
|
? ['error' => 'Kein Vorschlag generiert']
|
||||||
|
: ['connection_error' => true]
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
echo json_encode($vorschlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function resetVorschlag(array $params): void
|
public function resetVorschlag(array $params): void
|
||||||
|
|
|
||||||
|
|
@ -1101,6 +1101,16 @@ function _showKiBadgeError() {
|
||||||
'<button class="btn btn-secondary btn-sm" onclick="_startKiAnalyse()">Erneut versuchen</button></div>';
|
'<button class="btn btn-secondary btn-sm" onclick="_startKiAnalyse()">Erneut versuchen</button></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _showKiBadgeConnectionError() {
|
||||||
|
const neuBtn = _kiAktiv
|
||||||
|
? ' <button class="btn btn-secondary btn-sm" onclick="_startKiAnalyse()">Erneut versuchen</button>'
|
||||||
|
: '';
|
||||||
|
document.getElementById('ki-badge').innerHTML =
|
||||||
|
'<div class="flash flash-warning">Ollama nicht erreichbar.' + neuBtn +
|
||||||
|
' <a href="/settings" style="color:inherit;text-decoration:underline">Einstellungen / Verbindung testen</a>' +
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
|
|
||||||
// Normalisiert _kiVorschlag zu einem Array (Objekt oder Array aus DB möglich).
|
// Normalisiert _kiVorschlag zu einem Array (Objekt oder Array aus DB möglich).
|
||||||
// Gibt null zurück wenn kein Ergebnis vorhanden.
|
// Gibt null zurück wenn kein Ergebnis vorhanden.
|
||||||
// Gibt leeres Array [] zurück wenn verarbeitet aber kein Ergebnis erzeugt.
|
// Gibt leeres Array [] zurück wenn verarbeitet aber kein Ergebnis erzeugt.
|
||||||
|
|
@ -1119,6 +1129,7 @@ async function _startKiAnalyse() {
|
||||||
try {
|
try {
|
||||||
const r = await fetch('/belege/' + _belegId + '/vorschlag/generate', {method: 'POST'});
|
const r = await fetch('/belege/' + _belegId + '/vorschlag/generate', {method: 'POST'});
|
||||||
const raw = await r.json();
|
const raw = await r.json();
|
||||||
|
if (raw && raw.connection_error) { _showKiBadgeConnectionError(); return; }
|
||||||
const arr = _normalizeKiArr(raw);
|
const arr = _normalizeKiArr(raw);
|
||||||
if (arr && arr.length > 0) { _applyKiVorschlag(arr[0]); _showKiBadgeDone(arr); }
|
if (arr && arr.length > 0) { _applyKiVorschlag(arr[0]); _showKiBadgeDone(arr); }
|
||||||
else _showKiBadgeNoResult();
|
else _showKiBadgeNoResult();
|
||||||
|
|
|
||||||
54
tests/43_KiConnectionFallbackTest.php
Normal file
54
tests/43_KiConnectionFallbackTest.php
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Steuerkram\Database;
|
||||||
|
use Steuerkram\Models\Beleg;
|
||||||
|
|
||||||
|
class KiConnectionFallbackTest extends TestCase
|
||||||
|
{
|
||||||
|
protected string $testName = 'Test 43: KI-Verbindungsfehler-Fallback';
|
||||||
|
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$belegId = $this->createTestBeleg();
|
||||||
|
|
||||||
|
// [] = "verarbeitet, kein Ergebnis" – verhindert endlose Auto-Retry-Schleife
|
||||||
|
Beleg::saveKiVorschlag($belegId, []);
|
||||||
|
$beleg = Beleg::find($belegId);
|
||||||
|
$this->assertNotEmpty($beleg['ki_vorschlag'], 'Leeres Array muss als JSON gespeichert werden, nicht als SQL NULL');
|
||||||
|
|
||||||
|
$decoded = json_decode($beleg['ki_vorschlag'], true);
|
||||||
|
$this->assert(is_array($decoded) && count($decoded) === 0, 'Gespeicherter Wert muss leeres Array sein');
|
||||||
|
|
||||||
|
// null = "noch nicht verarbeitet" – löst Auto-Retry auf der Beleg-Seite aus
|
||||||
|
Beleg::saveKiVorschlag($belegId, null);
|
||||||
|
$beleg = Beleg::find($belegId);
|
||||||
|
$this->assert($beleg['ki_vorschlag'] === null, 'saveKiVorschlag(null) muss SQL NULL ergeben');
|
||||||
|
|
||||||
|
// Nicht-leeres Array wird korrekt gespeichert und zurückgelesen
|
||||||
|
$vorschlag = [[
|
||||||
|
'buchungstyp' => 'Ausgabe',
|
||||||
|
'kategorie_konto_id' => 5,
|
||||||
|
'zahlungskonto_id' => 2,
|
||||||
|
'ust_satz' => 19,
|
||||||
|
'betrag_netto' => 99.00,
|
||||||
|
'buchungstext' => 'Fallback-Test',
|
||||||
|
]];
|
||||||
|
Beleg::saveKiVorschlag($belegId, $vorschlag);
|
||||||
|
$beleg = Beleg::find($belegId);
|
||||||
|
$decoded = json_decode($beleg['ki_vorschlag'], true);
|
||||||
|
$this->assert(is_array($decoded) && count($decoded) === 1, 'Vorschlag-Array korrekt gespeichert');
|
||||||
|
$this->assertEquals('Ausgabe', $decoded[0]['buchungstyp'], 'buchungstyp korrekt');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createTestBeleg(): int
|
||||||
|
{
|
||||||
|
Database::execute(
|
||||||
|
"INSERT INTO belege (beleg_nr, filename, storage_path, mime_type, file_hash, belegtyp)
|
||||||
|
VALUES ('TEST-KI-FALLBACK-001', 'test.pdf', 'static/uploads/test/test.pdf', 'application/pdf',
|
||||||
|
SHA2('fallback-test', 256), 'Eingangsrechnung')"
|
||||||
|
);
|
||||||
|
return (int)Database::lastInsertId();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue