StreamExploration streams exploration events via SSE
(c *gin.Context)
| 42 | |
| 43 | // StreamExploration streams exploration events via SSE |
| 44 | func (h *Handler) StreamExploration(c *gin.Context) { |
| 45 | if h.explorer == nil { |
| 46 | c.JSON(http.StatusServiceUnavailable, gin.H{"error": "AI Explorer is not available"}) |
| 47 | return |
| 48 | } |
| 49 | |
| 50 | sessionID := c.Param("id") |
| 51 | session, ok := h.explorer.GetSession(sessionID) |
| 52 | if !ok { |
| 53 | c.JSON(http.StatusNotFound, gin.H{"error": "session not found"}) |
| 54 | return |
| 55 | } |
| 56 | |
| 57 | c.Header("Content-Type", "text/event-stream") |
| 58 | c.Header("Cache-Control", "no-cache") |
| 59 | c.Header("Connection", "keep-alive") |
| 60 | c.Header("X-Accel-Buffering", "no") |
| 61 | |
| 62 | flusher, ok := c.Writer.(http.Flusher) |
| 63 | if !ok { |
| 64 | c.JSON(http.StatusInternalServerError, gin.H{"error": "streaming not supported"}) |
| 65 | return |
| 66 | } |
| 67 | |
| 68 | clientGone := c.Request.Context().Done() |
| 69 | |
| 70 | for { |
| 71 | select { |
| 72 | case <-clientGone: |
| 73 | return |
| 74 | case event, ok := <-session.StreamChan: |
| 75 | if !ok { |
| 76 | data, _ := json.Marshal(map[string]string{"type": "done"}) |
| 77 | fmt.Fprintf(c.Writer, "data: %s\n\n", data) |
| 78 | flusher.Flush() |
| 79 | return |
| 80 | } |
| 81 | |
| 82 | data, err := json.Marshal(event) |
| 83 | if err != nil { |
| 84 | continue |
| 85 | } |
| 86 | fmt.Fprintf(c.Writer, "data: %s\n\n", data) |
| 87 | flusher.Flush() |
| 88 | |
| 89 | if event.Type == "done" { |
| 90 | return |
| 91 | } |
| 92 | case <-time.After(30 * time.Second): |
| 93 | fmt.Fprintf(c.Writer, ": keepalive\n\n") |
| 94 | flusher.Flush() |
| 95 | } |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | // StopExploration stops a running exploration session |
| 100 | func (h *Handler) StopExploration(c *gin.Context) { |
nothing calls this directly
no test coverage detected