Modification du timeout pour permettre d'analyser de plus gros livres
This commit is contained in:
@@ -66,7 +66,7 @@ from app.infrastructure.pdf_extractor import PyMuPdfTextExtractor
|
|||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="LoreMind Brain",
|
title="LoreMind Brain",
|
||||||
description="Backend IA pour la génération de contenu narratif.",
|
description="Backend IA pour la génération de contenu narratif.",
|
||||||
version="0.11.1-beta",
|
version="0.11.2-beta",
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<groupId>com.loremind</groupId>
|
<groupId>com.loremind</groupId>
|
||||||
<artifactId>loremind-core</artifactId>
|
<artifactId>loremind-core</artifactId>
|
||||||
<version>0.11.1-beta</version>
|
<version>0.11.2-beta</version>
|
||||||
<name>LoreMind Core</name>
|
<name>LoreMind Core</name>
|
||||||
<description>Backend Core - Architecture Hexagonale</description>
|
<description>Backend Core - Architecture Hexagonale</description>
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,10 @@ import java.util.Map;
|
|||||||
@RequestMapping("/api/notebooks")
|
@RequestMapping("/api/notebooks")
|
||||||
public class NotebookController {
|
public class NotebookController {
|
||||||
|
|
||||||
private static final long SSE_TIMEOUT_MS = 10 * 60 * 1000L;
|
// L'analyse approfondie (map-reduce sur tout le doc) peut être longue sur un gros
|
||||||
|
// livre / un modèle lent → 30 min. Pour aller plus vite : modèle gros-contexte
|
||||||
|
// (moins de lots). Au-delà, l'expiration est gérée proprement (pas de crash).
|
||||||
|
private static final long SSE_TIMEOUT_MS = 30 * 60 * 1000L;
|
||||||
|
|
||||||
private final NotebookService service;
|
private final NotebookService service;
|
||||||
private final NotebookChatStreamer chatStreamer;
|
private final NotebookChatStreamer chatStreamer;
|
||||||
@@ -143,13 +146,18 @@ public class NotebookController {
|
|||||||
return emitter;
|
return emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Helpers SSE (mêmes conventions que AiChatController) ---
|
// --- Helpers SSE ---
|
||||||
|
// IMPORTANT : on attrape AUSSI IllegalStateException. Si le flux a déjà été fermé
|
||||||
|
// (timeout async, client déconnecté), `emitter.send/complete` la lève — et comme
|
||||||
|
// ces helpers tournent dans un thread d'exécuteur, une exception non gérée y
|
||||||
|
// remontait jusqu'au pool ("Exception in thread task-1: ResponseBodyEmitter has
|
||||||
|
// already completed"). On l'ignore silencieusement : il n'y a plus rien à envoyer.
|
||||||
|
|
||||||
private void sendToken(SseEmitter emitter, String token) {
|
private void sendToken(SseEmitter emitter, String token) {
|
||||||
try {
|
try {
|
||||||
emitter.send(SseEmitter.event().name("token").data("{\"token\":" + jsonEscape(token) + "}"));
|
emitter.send(SseEmitter.event().name("token").data("{\"token\":" + jsonEscape(token) + "}"));
|
||||||
} catch (IOException e) {
|
} catch (IOException | IllegalStateException e) {
|
||||||
emitter.completeWithError(e);
|
// flux fermé/expiré : on cesse d'écrire
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,8 +165,8 @@ public class NotebookController {
|
|||||||
try {
|
try {
|
||||||
emitter.send(SseEmitter.event().name("progress")
|
emitter.send(SseEmitter.event().name("progress")
|
||||||
.data("{\"current\":" + p.current() + ",\"total\":" + p.total() + "}"));
|
.data("{\"current\":" + p.current() + ",\"total\":" + p.total() + "}"));
|
||||||
} catch (IOException e) {
|
} catch (IOException | IllegalStateException e) {
|
||||||
emitter.completeWithError(e);
|
// flux fermé/expiré : on cesse d'écrire
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,8 +174,8 @@ public class NotebookController {
|
|||||||
try {
|
try {
|
||||||
emitter.send(SseEmitter.event().name("done").data("{}"));
|
emitter.send(SseEmitter.event().name("done").data("{}"));
|
||||||
emitter.complete();
|
emitter.complete();
|
||||||
} catch (IOException e) {
|
} catch (IOException | IllegalStateException e) {
|
||||||
emitter.completeWithError(e);
|
// flux déjà fermé/expiré : rien à compléter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,8 +184,8 @@ public class NotebookController {
|
|||||||
String message = error.getMessage() != null ? error.getMessage() : error.getClass().getSimpleName();
|
String message = error.getMessage() != null ? error.getMessage() : error.getClass().getSimpleName();
|
||||||
emitter.send(SseEmitter.event().name("error").data("{\"message\":" + jsonEscape(message) + "}"));
|
emitter.send(SseEmitter.event().name("error").data("{\"message\":" + jsonEscape(message) + "}"));
|
||||||
emitter.complete();
|
emitter.complete();
|
||||||
} catch (IOException ioe) {
|
} catch (IOException | IllegalStateException e) {
|
||||||
emitter.completeWithError(ioe);
|
// flux déjà fermé/expiré : rien à envoyer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
web/package-lock.json
generated
4
web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "loremind-web",
|
"name": "loremind-web",
|
||||||
"version": "0.11.1-beta",
|
"version": "0.11.2-beta",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "loremind-web",
|
"name": "loremind-web",
|
||||||
"version": "0.11.1-beta",
|
"version": "0.11.2-beta",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^17.0.0",
|
"@angular/animations": "^17.0.0",
|
||||||
"@angular/common": "^17.0.0",
|
"@angular/common": "^17.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "loremind-web",
|
"name": "loremind-web",
|
||||||
"version": "0.11.1-beta",
|
"version": "0.11.2-beta",
|
||||||
"description": "LoreMind Frontend - Angular",
|
"description": "LoreMind Frontend - Angular",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
|
|||||||
Reference in New Issue
Block a user