package prediction import ( "sync" ) type Event struct { Status string `json:"status"` Logs string `json:"logs,omitempty"` Metrics map[string]any `json:"metrics,omitempty"` Output any `json:"output,omitempty"` Error string `json:"error,omitempty"` Done bool `json:"-"` } type Hub struct { mu sync.RWMutex subs map[string]map[chan Event]struct{} } func NewHub() *Hub { return &Hub{subs: make(map[string]map[chan Event]struct{})} } func (h *Hub) Subscribe(predictionID string) chan Event { ch := make(chan Event, 16) h.mu.Lock() if h.subs[predictionID] == nil { h.subs[predictionID] = make(map[chan Event]struct{}) } h.subs[predictionID][ch] = struct{}{} h.mu.Unlock() return ch } func (h *Hub) Unsubscribe(predictionID string, ch chan Event) { h.mu.Lock() if m := h.subs[predictionID]; m != nil { delete(m, ch) if len(m) == 0 { delete(h.subs, predictionID) } } h.mu.Unlock() close(ch) } func (h *Hub) Publish(predictionID string, ev Event) { h.mu.RLock() defer h.mu.RUnlock() for ch := range h.subs[predictionID] { select { case ch <- ev: default: } } }