|
28 | 28 | * <ul> |
29 | 29 | * <li>{@code POST /api/dashboards/generate} — blocking; returns the validated config.</li> |
30 | 30 | * <li>{@code POST /api/dashboards/generate/stream} — SSE; streams the agent's live |
31 | | - * steps ({@code step} events: grounding → planning → validating) then a {@code done} |
32 | | - * event with the final config (or an {@code error} event).</li> |
| 31 | + * steps ({@code step} events: grounding → planning → validating) then either a |
| 32 | + * {@code chat} event (out-of-context reply) or a {@code done} event with the |
| 33 | + * artifact config (or an {@code error} event).</li> |
33 | 34 | * </ul> |
34 | 35 | * |
35 | 36 | * Read-only: generates a config and validates queries by running them read-only; it never |
@@ -101,8 +102,20 @@ public SseEmitter generateStream(@RequestBody GenerateRequest request) { |
101 | 102 | throw new ClientGoneException(io); |
102 | 103 | } |
103 | 104 | }); |
104 | | - emitter.send(SseEmitter.event().name("done") |
105 | | - .data(Map.of("success", true, "dashboardConfig", config))); |
| 105 | + // Chat-only replies (greetings / tool questions) must not share the |
| 106 | + // `done` event with a real artifact — the FE's done handler always |
| 107 | + // appends "Done — built…" and auto-saves. A dedicated `chat` event |
| 108 | + // keeps that path from swallowing out-of-context messages. |
| 109 | + if (Boolean.TRUE.equals(config.get("chat"))) { |
| 110 | + emitter.send(SseEmitter.event().name("chat") |
| 111 | + .data(Map.of( |
| 112 | + "success", true, |
| 113 | + "reply", String.valueOf(config.getOrDefault("reply", "")), |
| 114 | + "dashboardConfig", config))); |
| 115 | + } else { |
| 116 | + emitter.send(SseEmitter.event().name("done") |
| 117 | + .data(Map.of("success", true, "dashboardConfig", config))); |
| 118 | + } |
106 | 119 | emitter.complete(); |
107 | 120 | } catch (ClientGoneException gone) { |
108 | 121 | emitter.complete(); |
|
0 commit comments