Amélioration du feedback pendant les imports sur les PDF
All checks were successful
All checks were successful
passage en 0.12.4-beta
This commit is contained in:
@@ -65,10 +65,11 @@ public class CampaignImportService {
|
||||
String filename,
|
||||
Consumer<CampaignImportProgress> onProgress,
|
||||
Runnable onHeartbeat,
|
||||
Consumer<String> onStatus,
|
||||
Consumer<CampaignImportProposal> onDone,
|
||||
Consumer<Throwable> onError) {
|
||||
campaignPdfImporter.importCampaignStreaming(
|
||||
pdfBytes, filename, onProgress, onHeartbeat, onDone, onError);
|
||||
pdfBytes, filename, onProgress, onHeartbeat, onStatus, onDone, onError);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,10 +40,11 @@ public class GameSystemService {
|
||||
String filename,
|
||||
java.util.function.Consumer<com.loremind.domain.gamesystemcontext.RulesImportProgress> onProgress,
|
||||
Runnable onHeartbeat,
|
||||
java.util.function.Consumer<String> onStatus,
|
||||
java.util.function.Consumer<RulesImportResult> onDone,
|
||||
java.util.function.Consumer<Throwable> onError) {
|
||||
rulesPdfImporter.importRulesStreaming(
|
||||
pdfBytes, filename, onProgress, onHeartbeat, onDone, onError);
|
||||
pdfBytes, filename, onProgress, onHeartbeat, onStatus, onDone, onError);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,10 @@ public interface CampaignPdfImporter {
|
||||
* @param onHeartbeat invoqué périodiquement pendant un appel LLM long (aucune
|
||||
* avancée à afficher, mais le canal SSE vers le navigateur
|
||||
* doit rester actif — sinon un proxy intermédiaire le coupe).
|
||||
* @param onStatus invoqué avec un message lisible quand quelque chose se
|
||||
* passe pendant l'attente (fournisseur saturé → retry,
|
||||
* morceau re-découpé, morceau ignoré…) — affiché par l'UI
|
||||
* pour que l'utilisateur n'ait pas à lire les logs.
|
||||
* @param onDone invoqué une fois avec l'arbre proposé (non persisté).
|
||||
* @param onError invoqué si l'extraction/structuration échoue.
|
||||
*/
|
||||
@@ -27,6 +31,7 @@ public interface CampaignPdfImporter {
|
||||
String filename,
|
||||
Consumer<CampaignImportProgress> onProgress,
|
||||
Runnable onHeartbeat,
|
||||
Consumer<String> onStatus,
|
||||
Consumer<CampaignImportProposal> onDone,
|
||||
Consumer<Throwable> onError);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@ public interface RulesPdfImporter {
|
||||
* @param onHeartbeat invoqué périodiquement pendant un appel LLM long (aucune
|
||||
* avancée à afficher, mais le canal SSE vers le navigateur
|
||||
* doit rester actif — sinon un proxy intermédiaire le coupe).
|
||||
* @param onStatus invoqué avec un message lisible quand quelque chose se
|
||||
* passe pendant l'attente (fournisseur saturé → retry,
|
||||
* morceau re-découpé, morceau ignoré…) — affiché par l'UI
|
||||
* pour que l'utilisateur n'ait pas à lire les logs.
|
||||
* @param onDone invoqué une fois avec le résultat final.
|
||||
* @param onError invoqué si l'extraction/structuration échoue.
|
||||
*/
|
||||
@@ -38,6 +42,7 @@ public interface RulesPdfImporter {
|
||||
String filename,
|
||||
Consumer<RulesImportProgress> onProgress,
|
||||
Runnable onHeartbeat,
|
||||
Consumer<String> onStatus,
|
||||
Consumer<RulesImportResult> onDone,
|
||||
Consumer<Throwable> onError);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ public class BrainCampaignImportClient implements CampaignPdfImporter {
|
||||
String filename,
|
||||
Consumer<CampaignImportProgress> onProgress,
|
||||
Runnable onHeartbeat,
|
||||
Consumer<String> onStatus,
|
||||
Consumer<CampaignImportProposal> onDone,
|
||||
Consumer<Throwable> onError) {
|
||||
|
||||
@@ -85,7 +86,7 @@ public class BrainCampaignImportClient implements CampaignPdfImporter {
|
||||
.timeout(Duration.ofSeconds(importTimeoutSeconds))
|
||||
.doOnNext(sse -> handleEvent(
|
||||
sse, pageCount, ocrPageCount, terminated,
|
||||
onProgress, onHeartbeat, onDone, onError))
|
||||
onProgress, onHeartbeat, onStatus, onDone, onError))
|
||||
.blockLast();
|
||||
if (!terminated[0]) {
|
||||
onError.accept(new CampaignImportException(
|
||||
@@ -110,6 +111,7 @@ public class BrainCampaignImportClient implements CampaignPdfImporter {
|
||||
boolean[] terminated,
|
||||
Consumer<CampaignImportProgress> onProgress,
|
||||
Runnable onHeartbeat,
|
||||
Consumer<String> onStatus,
|
||||
Consumer<CampaignImportProposal> onDone,
|
||||
Consumer<Throwable> onError) {
|
||||
|
||||
@@ -122,6 +124,22 @@ public class BrainCampaignImportClient implements CampaignPdfImporter {
|
||||
onHeartbeat.run();
|
||||
return;
|
||||
}
|
||||
if ("status".equals(event)) {
|
||||
// Message d'attente lisible (retry sur fournisseur saturé, morceau
|
||||
// re-découpé…) : affiché par l'UI au lieu de n'exister qu'en logs.
|
||||
onStatus.accept(readMessage(data));
|
||||
return;
|
||||
}
|
||||
if ("chunk_failed".equals(event)) {
|
||||
JsonNode node = readJson(data);
|
||||
String msg = node != null && node.hasNonNull("message")
|
||||
? node.get("message").asText() : "";
|
||||
int current = node != null ? node.path("current").asInt() : 0;
|
||||
int total = node != null ? node.path("total").asInt() : 0;
|
||||
onStatus.accept("Morceau " + current + "/" + total + " ignoré"
|
||||
+ (msg.isEmpty() ? "." : " : " + msg));
|
||||
return;
|
||||
}
|
||||
if ("error".equals(event)) {
|
||||
terminated[0] = true;
|
||||
onError.accept(new CampaignImportException(
|
||||
|
||||
@@ -115,6 +115,7 @@ public class BrainRulesImportClient implements RulesPdfImporter {
|
||||
String filename,
|
||||
Consumer<RulesImportProgress> onProgress,
|
||||
Runnable onHeartbeat,
|
||||
Consumer<String> onStatus,
|
||||
Consumer<RulesImportResult> onDone,
|
||||
Consumer<Throwable> onError) {
|
||||
|
||||
@@ -141,7 +142,7 @@ public class BrainRulesImportClient implements RulesPdfImporter {
|
||||
.timeout(Duration.ofSeconds(importTimeoutSeconds))
|
||||
.doOnNext(sse -> handleEvent(
|
||||
sse, pageCount, ocrPageCount, terminated,
|
||||
onProgress, onHeartbeat, onDone, onError))
|
||||
onProgress, onHeartbeat, onStatus, onDone, onError))
|
||||
.blockLast();
|
||||
// Flux terminé sans event done/error (ex: connexion coupée) → on signale.
|
||||
if (!terminated[0]) {
|
||||
@@ -168,6 +169,7 @@ public class BrainRulesImportClient implements RulesPdfImporter {
|
||||
boolean[] terminated,
|
||||
Consumer<RulesImportProgress> onProgress,
|
||||
Runnable onHeartbeat,
|
||||
Consumer<String> onStatus,
|
||||
Consumer<RulesImportResult> onDone,
|
||||
Consumer<Throwable> onError) {
|
||||
|
||||
@@ -181,6 +183,22 @@ public class BrainRulesImportClient implements RulesPdfImporter {
|
||||
onHeartbeat.run();
|
||||
return;
|
||||
}
|
||||
if ("status".equals(event)) {
|
||||
// Message d'attente lisible (retry sur fournisseur saturé, morceau
|
||||
// re-découpé…) : affiché par l'UI au lieu de n'exister qu'en logs.
|
||||
onStatus.accept(readMessage(data));
|
||||
return;
|
||||
}
|
||||
if ("chunk_failed".equals(event)) {
|
||||
JsonNode node = readJson(data);
|
||||
String msg = node != null && node.hasNonNull("message")
|
||||
? node.get("message").asText() : "";
|
||||
int current = node != null ? node.path("current").asInt() : 0;
|
||||
int total = node != null ? node.path("total").asInt() : 0;
|
||||
onStatus.accept("Morceau " + current + "/" + total + " ignoré"
|
||||
+ (msg.isEmpty() ? "." : " : " + msg));
|
||||
return;
|
||||
}
|
||||
if ("error".equals(event)) {
|
||||
terminated[0] = true;
|
||||
onError.accept(new RulesImportException(
|
||||
|
||||
@@ -88,6 +88,8 @@ public class CampaignImportController {
|
||||
bytes, filename,
|
||||
progress -> sendEvent(emitter, clientGone, "progress", progress),
|
||||
() -> sendHeartbeat(emitter, clientGone),
|
||||
status -> sendEvent(emitter, clientGone, "status",
|
||||
Map.of("message", status != null ? status : "")),
|
||||
proposal -> {
|
||||
sendEvent(emitter, clientGone, "done", proposal);
|
||||
emitter.complete();
|
||||
|
||||
@@ -170,6 +170,8 @@ public class GameSystemController {
|
||||
bytes, filename,
|
||||
progress -> sendImportEvent(emitter, clientGone, "progress", progress),
|
||||
() -> sendImportHeartbeat(emitter, clientGone),
|
||||
status -> sendImportEvent(emitter, clientGone, "status",
|
||||
Map.of("message", status != null ? status : "")),
|
||||
result -> {
|
||||
sendImportEvent(emitter, clientGone, "done", result);
|
||||
emitter.complete();
|
||||
|
||||
Reference in New Issue
Block a user