#88: Fehlerursache bei FAILED_FINAL im Verlauf-Tab anzeigen (Fall A)
Schema-Analyse ergab Fall A: failure_message ist bereits in V1 vorhanden und wird persistiert. Keine Flyway-Migration notwendig. - GuiHistoryTab: TextArea 'Fehlerursache' ergaenzt; zeigt failure_message des letzten Fehler-Attempts bei FAILED_FINAL, FAILED_RETRYABLE, SKIPPED_FINAL_FAILURE; promptText-Platzhalter bei NULL/leer - SqliteProcessingAttemptRepositoryAdapter: 1000-Zeichen-Limit fuer failure_message vor Persistierung erzwungen (mit Kuerzungsmarkierung) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+45
@@ -127,6 +127,7 @@ public final class GuiHistoryTab {
|
|||||||
|
|
||||||
private final TableView<ProcessingAttempt> attemptsTable = new TableView<>();
|
private final TableView<ProcessingAttempt> attemptsTable = new TableView<>();
|
||||||
private final ObservableList<ProcessingAttempt> attemptsItems = FXCollections.observableArrayList();
|
private final ObservableList<ProcessingAttempt> attemptsItems = FXCollections.observableArrayList();
|
||||||
|
private final TextArea failureArea = new TextArea();
|
||||||
private final TextArea reasoningArea = new TextArea();
|
private final TextArea reasoningArea = new TextArea();
|
||||||
|
|
||||||
private final Button resetButton = new Button("Status zurücksetzen");
|
private final Button resetButton = new Button("Status zurücksetzen");
|
||||||
@@ -339,6 +340,14 @@ public final class GuiHistoryTab {
|
|||||||
Label attemptsTitle = new Label("Verarbeitungsversuche");
|
Label attemptsTitle = new Label("Verarbeitungsversuche");
|
||||||
attemptsTitle.setStyle("-fx-font-weight: bold;");
|
attemptsTitle.setStyle("-fx-font-weight: bold;");
|
||||||
|
|
||||||
|
// Fehlerursache (aus letztem Fehler-Versuch)
|
||||||
|
failureArea.setEditable(false);
|
||||||
|
failureArea.setWrapText(true);
|
||||||
|
failureArea.setPrefRowCount(3);
|
||||||
|
failureArea.setPromptText("Keine Fehlerdetails gespeichert.");
|
||||||
|
Label failureTitle = new Label("Fehlerursache (letzter Fehler-Versuch)");
|
||||||
|
failureTitle.setStyle("-fx-font-weight: bold;");
|
||||||
|
|
||||||
// KI-Begründung
|
// KI-Begründung
|
||||||
reasoningArea.setEditable(false);
|
reasoningArea.setEditable(false);
|
||||||
reasoningArea.setWrapText(true);
|
reasoningArea.setWrapText(true);
|
||||||
@@ -350,6 +359,7 @@ public final class GuiHistoryTab {
|
|||||||
VBox rightPane = new VBox(8,
|
VBox rightPane = new VBox(8,
|
||||||
detailTitle, detailGrid,
|
detailTitle, detailGrid,
|
||||||
attemptsTitle, attemptsTable,
|
attemptsTitle, attemptsTable,
|
||||||
|
failureTitle, failureArea,
|
||||||
reasoningTitle, reasoningArea);
|
reasoningTitle, reasoningArea);
|
||||||
rightPane.setPadding(new Insets(4, 8, 4, 4));
|
rightPane.setPadding(new Insets(4, 8, 4, 4));
|
||||||
VBox.setVgrow(attemptsTable, Priority.ALWAYS);
|
VBox.setVgrow(attemptsTable, Priority.ALWAYS);
|
||||||
@@ -658,6 +668,9 @@ public final class GuiHistoryTab {
|
|||||||
|
|
||||||
attemptsItems.setAll(result.attempts());
|
attemptsItems.setAll(result.attempts());
|
||||||
|
|
||||||
|
// Fehlerursache aus letztem Fehler-Versuch anzeigen
|
||||||
|
showLastFailureMessage(result.attempts(), record.overallStatus());
|
||||||
|
|
||||||
// Neuesten Versuch selektieren und Begründung anzeigen
|
// Neuesten Versuch selektieren und Begründung anzeigen
|
||||||
if (!result.attempts().isEmpty()) {
|
if (!result.attempts().isEmpty()) {
|
||||||
ProcessingAttempt last = result.attempts().get(result.attempts().size() - 1);
|
ProcessingAttempt last = result.attempts().get(result.attempts().size() - 1);
|
||||||
@@ -676,6 +689,36 @@ public final class GuiHistoryTab {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeigt die Fehlerursache des letzten Fehlschlags im Fehlerursache-Bereich an.
|
||||||
|
* Relevant bei Status FAILED_FINAL, FAILED_RETRYABLE und SKIPPED_FINAL_FAILURE.
|
||||||
|
* Bei fehlendem Eintrag oder leerem Feld wird ein Platzhalter-Text gesetzt.
|
||||||
|
*/
|
||||||
|
private void showLastFailureMessage(List<ProcessingAttempt> attempts, ProcessingStatus overallStatus) {
|
||||||
|
boolean failureRelevant = overallStatus == ProcessingStatus.FAILED_FINAL
|
||||||
|
|| overallStatus == ProcessingStatus.FAILED_RETRYABLE
|
||||||
|
|| overallStatus == ProcessingStatus.SKIPPED_FINAL_FAILURE;
|
||||||
|
|
||||||
|
if (!failureRelevant || attempts.isEmpty()) {
|
||||||
|
failureArea.setText("");
|
||||||
|
failureArea.setPromptText("Keine Fehlerdetails für diesen Status.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Letzten Versuch mit nicht-leerem failure_message suchen (absteigend nach attempt_number)
|
||||||
|
String failureMessage = null;
|
||||||
|
for (int i = attempts.size() - 1; i >= 0; i--) {
|
||||||
|
String msg = attempts.get(i).failureMessage();
|
||||||
|
if (msg != null && !msg.isBlank()) {
|
||||||
|
failureMessage = msg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
failureArea.setText(failureMessage != null ? failureMessage : "");
|
||||||
|
failureArea.setPromptText("Keine Fehlerdetails gespeichert.");
|
||||||
|
}
|
||||||
|
|
||||||
private void showReasoning(ProcessingAttempt attempt) {
|
private void showReasoning(ProcessingAttempt attempt) {
|
||||||
String reasoning = attempt.aiReasoning();
|
String reasoning = attempt.aiReasoning();
|
||||||
reasoningArea.setText(reasoning != null && !reasoning.isBlank()
|
reasoningArea.setText(reasoning != null && !reasoning.isBlank()
|
||||||
@@ -685,6 +728,8 @@ public final class GuiHistoryTab {
|
|||||||
private void clearDetailPane() {
|
private void clearDetailPane() {
|
||||||
clearDetailFields();
|
clearDetailFields();
|
||||||
attemptsItems.clear();
|
attemptsItems.clear();
|
||||||
|
failureArea.setText("");
|
||||||
|
failureArea.setPromptText("Keine Fehlerdetails gespeichert.");
|
||||||
reasoningArea.setText(DETAIL_PLACEHOLDER);
|
reasoningArea.setText(DETAIL_PLACEHOLDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-1
@@ -159,7 +159,8 @@ public class SqliteProcessingAttemptRepositoryAdapter implements ProcessingAttem
|
|||||||
statement.setString(5, attempt.endedAt().toString());
|
statement.setString(5, attempt.endedAt().toString());
|
||||||
statement.setString(6, attempt.status().name());
|
statement.setString(6, attempt.status().name());
|
||||||
setNullableString(statement, 7, attempt.failureClass());
|
setNullableString(statement, 7, attempt.failureClass());
|
||||||
setNullableString(statement, 8, attempt.failureMessage());
|
// 1000-Zeichen-Grenze erzwingen; längere Meldungen werden mit „…" markiert
|
||||||
|
setNullableString(statement, 8, truncateFailureMessage(attempt.failureMessage()));
|
||||||
statement.setBoolean(9, attempt.retryable());
|
statement.setBoolean(9, attempt.retryable());
|
||||||
// AI provider identifier and AI traceability fields
|
// AI provider identifier and AI traceability fields
|
||||||
setNullableString(statement, 10, attempt.aiProvider());
|
setNullableString(statement, 10, attempt.aiProvider());
|
||||||
@@ -360,6 +361,27 @@ public class SqliteProcessingAttemptRepositoryAdapter implements ProcessingAttem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Hilfsmethoden
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kürzt eine Fehlermeldung auf maximal 1000 Zeichen vor der Persistierung.
|
||||||
|
* Längere Meldungen werden mit „…" markiert.
|
||||||
|
*
|
||||||
|
* @param message die ursprüngliche Fehlermeldung; kann {@code null} sein
|
||||||
|
* @return die (ggf. gekürzte) Meldung oder {@code null}
|
||||||
|
*/
|
||||||
|
private static String truncateFailureMessage(String message) {
|
||||||
|
if (message == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (message.length() <= 1000) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
return message.substring(0, 997) + "…";
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// JDBC nullable helpers
|
// JDBC nullable helpers
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user