Erweiterungen
This commit is contained in:
34
Dockerfile
34
Dockerfile
@@ -1,30 +1,4 @@
|
|||||||
FROM ghcr.io/jqlang/jq:latest AS jq-stage
|
FROM eclipse-temurin:21-jre
|
||||||
|
COPY target/*.jar app.jar
|
||||||
FROM eclipse-temurin:21-jdk AS build
|
EXPOSE 8080
|
||||||
COPY --from=jq-stage /jq /usr/bin/jq
|
ENTRYPOINT ["java", "-jar", "/app.jar", "--spring.profiles.active=production"]
|
||||||
# Test that jq works after copying
|
|
||||||
RUN jq --version
|
|
||||||
|
|
||||||
ENV HOME=/app
|
|
||||||
RUN mkdir -p $HOME
|
|
||||||
WORKDIR $HOME
|
|
||||||
COPY . $HOME
|
|
||||||
|
|
||||||
# If you have a Vaadin Pro key, pass it as a secret with id "proKey":
|
|
||||||
#
|
|
||||||
# $ docker build --secret id=proKey,src=$HOME/.vaadin/proKey .
|
|
||||||
#
|
|
||||||
# If you have a Vaadin Offline key, pass it as a secret with id "offlineKey":
|
|
||||||
#
|
|
||||||
# $ docker build --secret id=offlineKey,src=$HOME/.vaadin/offlineKey .
|
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/root/.m2 \
|
|
||||||
--mount=type=secret,id=proKey \
|
|
||||||
--mount=type=secret,id=offlineKey \
|
|
||||||
sh -c 'PRO_KEY=$(jq -r ".proKey // empty" /run/secrets/proKey 2>/dev/null || echo "") && \
|
|
||||||
OFFLINE_KEY=$(cat /run/secrets/offlineKey 2>/dev/null || echo "") && \
|
|
||||||
./mvnw clean package -DskipTests -Dvaadin.proKey=${PRO_KEY} -Dvaadin.offlineKey=${OFFLINE_KEY}'
|
|
||||||
|
|
||||||
FROM eclipse-temurin:21-jre-alpine
|
|
||||||
COPY --from=build /app/target/*.jar app.jar
|
|
||||||
ENTRYPOINT ["java", "-jar", "/app.jar", "--spring.profiles.active=prod"]
|
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@@ -4,7 +4,7 @@
|
|||||||
<!-- Project from https://start.vaadin.com/65cb55a1-ddc8-4889-a49a-7208c5f943e7 -->
|
<!-- Project from https://start.vaadin.com/65cb55a1-ddc8-4889-a49a-7208c5f943e7 -->
|
||||||
<groupId>de.assecutor.aimailassistant</groupId>
|
<groupId>de.assecutor.aimailassistant</groupId>
|
||||||
<artifactId>aimailassistant</artifactId>
|
<artifactId>aimailassistant</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>0.8.0</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ public class OrderEmail {
|
|||||||
|
|
||||||
private boolean deleted;
|
private boolean deleted;
|
||||||
|
|
||||||
|
private boolean analyzing;
|
||||||
|
|
||||||
@Column(columnDefinition = "TEXT")
|
@Column(columnDefinition = "TEXT")
|
||||||
private String summaryJson;
|
private String summaryJson;
|
||||||
|
|
||||||
@@ -117,6 +119,14 @@ public class OrderEmail {
|
|||||||
this.deleted = deleted;
|
this.deleted = deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAnalyzing() {
|
||||||
|
return analyzing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAnalyzing(boolean analyzing) {
|
||||||
|
this.analyzing = analyzing;
|
||||||
|
}
|
||||||
|
|
||||||
public String getSummaryJson() {
|
public String getSummaryJson() {
|
||||||
return summaryJson;
|
return summaryJson;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,23 +304,31 @@ public class ImapEmailService {
|
|||||||
orderEmail.setFromAddress("unknown@unknown.com");
|
orderEmail.setFromAddress("unknown@unknown.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save first to get ID
|
// Save first with analyzing=true
|
||||||
|
orderEmail.setAnalyzing(true);
|
||||||
orderEmail = orderEmailRepository.save(orderEmail);
|
orderEmail = orderEmailRepository.save(orderEmail);
|
||||||
|
|
||||||
|
// Notify UI about new email (shows "Analyse läuft")
|
||||||
|
EmailBroadcaster.broadcast();
|
||||||
|
|
||||||
// Process with LLM
|
// Process with LLM
|
||||||
try {
|
try {
|
||||||
OrderSummary summary = llmService.summarizeEmail(orderEmail);
|
OrderSummary summary = llmService.summarizeEmail(orderEmail);
|
||||||
orderEmail.setSummaryJson(llmService.serializeSummary(summary));
|
orderEmail.setSummaryJson(llmService.serializeSummary(summary));
|
||||||
orderEmail.setType(summary.getOrderType());
|
orderEmail.setType(summary.getOrderType());
|
||||||
|
orderEmail.setAnalyzing(false);
|
||||||
// processed bleibt false - wird erst gesetzt wenn der Benutzer die Email öffnet
|
// processed bleibt false - wird erst gesetzt wenn der Benutzer die Email öffnet
|
||||||
orderEmailRepository.save(orderEmail);
|
orderEmailRepository.save(orderEmail);
|
||||||
log.info("Email processed successfully: {} (Type: {})", subject, summary.getOrderType());
|
log.info("Email processed successfully: {} (Type: {})", subject, summary.getOrderType());
|
||||||
|
|
||||||
// Notify UI about new email
|
// Notify UI that analysis is complete
|
||||||
EmailBroadcaster.broadcast();
|
EmailBroadcaster.broadcast();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error processing email with LLM: {}", subject, e);
|
log.error("Error processing email with LLM: {}", subject, e);
|
||||||
// Email is saved but not processed - still notify UI
|
// Mark as no longer analyzing even on error
|
||||||
|
orderEmail.setAnalyzing(false);
|
||||||
|
orderEmailRepository.save(orderEmail);
|
||||||
|
// Notify UI
|
||||||
EmailBroadcaster.broadcast();
|
EmailBroadcaster.broadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -186,7 +186,11 @@ public class MainView extends VerticalLayout {
|
|||||||
String bgClass;
|
String bgClass;
|
||||||
String textClass;
|
String textClass;
|
||||||
|
|
||||||
if (email.isConfirmed()) {
|
if (email.isAnalyzing()) {
|
||||||
|
statusText = "Analyse läuft";
|
||||||
|
bgClass = LumoUtility.Background.CONTRAST_10;
|
||||||
|
textClass = LumoUtility.TextColor.SECONDARY;
|
||||||
|
} else if (email.isConfirmed()) {
|
||||||
statusText = "Bearbeitet";
|
statusText = "Bearbeitet";
|
||||||
bgClass = LumoUtility.Background.SUCCESS_10;
|
bgClass = LumoUtility.Background.SUCCESS_10;
|
||||||
textClass = LumoUtility.TextColor.SUCCESS;
|
textClass = LumoUtility.TextColor.SUCCESS;
|
||||||
@@ -248,6 +252,12 @@ public class MainView extends VerticalLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void openDetailDialog(OrderEmail email) {
|
private void openDetailDialog(OrderEmail email) {
|
||||||
|
if (email.isAnalyzing()) {
|
||||||
|
Notification.show("Die Email wird noch analysiert. Bitte warten Sie, bis die Analyse abgeschlossen ist.",
|
||||||
|
3000, Notification.Position.MIDDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
OrderDetailDialog dialog = new OrderDetailDialog(
|
OrderDetailDialog dialog = new OrderDetailDialog(
|
||||||
email,
|
email,
|
||||||
llmService,
|
llmService,
|
||||||
|
|||||||
@@ -1063,14 +1063,17 @@ public class OrderDetailDialog extends Dialog {
|
|||||||
// Read-only mode: only show close and delete buttons
|
// Read-only mode: only show close and delete buttons
|
||||||
getFooter().add(deleteButton, spacer, closeButton);
|
getFooter().add(deleteButton, spacer, closeButton);
|
||||||
} else {
|
} else {
|
||||||
|
Button reanalyzeButton = new Button("Erneut analysieren", new Icon(VaadinIcon.REFRESH), e -> reanalyzeEmail());
|
||||||
|
reanalyzeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||||
|
|
||||||
if (orderEmail.getType() == EmailType.QUOTE_REQUEST) {
|
if (orderEmail.getType() == EmailType.QUOTE_REQUEST) {
|
||||||
Button sendOfferButton = new Button("Angebot senden", e -> sendOffer());
|
Button sendOfferButton = new Button("Angebot senden", e -> sendOffer());
|
||||||
sendOfferButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
sendOfferButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
getFooter().add(deleteButton, spacer, closeButton, sendOfferButton);
|
getFooter().add(deleteButton, reanalyzeButton, spacer, closeButton, sendOfferButton);
|
||||||
} else {
|
} else {
|
||||||
Button acceptButton = new Button("Auftrag annehmen", e -> acceptOrder());
|
Button acceptButton = new Button("Auftrag annehmen", e -> acceptOrder());
|
||||||
acceptButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
acceptButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
getFooter().add(deleteButton, spacer, closeButton, acceptButton);
|
getFooter().add(deleteButton, reanalyzeButton, spacer, closeButton, acceptButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1115,6 +1118,53 @@ public class OrderDetailDialog extends Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void reanalyzeEmail() {
|
||||||
|
Notification loadingNotification = Notification.show(
|
||||||
|
"Email wird erneut analysiert...",
|
||||||
|
0,
|
||||||
|
Notification.Position.MIDDLE
|
||||||
|
);
|
||||||
|
|
||||||
|
getUI().ifPresent(ui -> {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
OrderSummary newSummary = llmService.summarizeEmail(orderEmail);
|
||||||
|
|
||||||
|
ui.access(() -> {
|
||||||
|
loadingNotification.close();
|
||||||
|
|
||||||
|
if (newSummary != null) {
|
||||||
|
summary = newSummary;
|
||||||
|
orderEmail.setSummaryJson(llmService.serializeSummary(summary));
|
||||||
|
orderEmail.setType(summary.getOrderType());
|
||||||
|
onProcessed.accept(orderEmail);
|
||||||
|
|
||||||
|
// UI komplett neu aufbauen
|
||||||
|
remove(contentLayout);
|
||||||
|
getFooter().removeAll();
|
||||||
|
contentLayout = createContent();
|
||||||
|
add(contentLayout);
|
||||||
|
createFooter();
|
||||||
|
|
||||||
|
Notification.show("Analyse abgeschlossen", 3000, Notification.Position.BOTTOM_START);
|
||||||
|
} else {
|
||||||
|
Notification.show("Analyse fehlgeschlagen", 3000, Notification.Position.MIDDLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
ui.access(() -> {
|
||||||
|
loadingNotification.close();
|
||||||
|
Notification.show(
|
||||||
|
"Fehler bei der Analyse: " + e.getMessage(),
|
||||||
|
5000,
|
||||||
|
Notification.Position.MIDDLE
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void sendOffer() {
|
private void sendOffer() {
|
||||||
// Validierung prüfen
|
// Validierung prüfen
|
||||||
if (binder != null && !binder.isValid()) {
|
if (binder != null && !binder.isValid()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user