Files
Agent-System-Backend/internal/bus/bus.go
T
weichen f1302ed5b3 Enable canonical versioned Go module consumption
Why: Allow consumers to download an immutable module revision without nested source checkouts or local path replacements.

Evidence: SELF package tests and the prior canonical-path rejection are recorded in the Dev dependency-workspace task card; remote consumption is replayed against the committed candidate.

Impact: Change the module identity and its Go imports while preserving API behavior. Existing historical submodule revisions remain buildable.

Refs: Versioned dependencies U1/U3; feature/dependency-workspace
2026-10-13 15:47:16 +00:00

76 lines
1.7 KiB
Go

// Package bus 是 streamhub:按会话分发已持久化的事件(SSE 通道)。
// 事件权威在 store(seq 游标);bus 只做在线转发。
// 订阅缓冲满时关闭该订阅,客户端用 Last-Event-ID 从游标重放,不再静默丢事件。
package bus
import (
"sync"
core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git"
)
type subscriber struct {
ch chan core.StreamEvent
filter string // conversationID;空 = 全部
closed bool
}
type Bus struct {
mu sync.Mutex
subs map[int64]*subscriber
next int64
buf int
}
func New() *Bus { return NewWithBuffer(512) }
// NewWithBuffer 指定订阅缓冲长度。测试用小缓冲观察溢出关闭。
func NewWithBuffer(n int) *Bus {
if n < 1 {
n = 1
}
return &Bus{subs: map[int64]*subscriber{}, buf: n}
}
// Subscribe 返回新订阅;close(unsubscribe) 注销。注销是幂等的。
func (b *Bus) Subscribe(conversationID string) (<-chan core.StreamEvent, func()) {
b.mu.Lock()
id := b.next
b.next++
ch := make(chan core.StreamEvent, b.buf)
b.subs[id] = &subscriber{ch: ch, filter: conversationID}
b.mu.Unlock()
return ch, func() {
b.mu.Lock()
b.dropLocked(id)
b.mu.Unlock()
}
}
// Publish 非阻塞广播。缓冲满的订阅被移除并关闭,避免静默丢事件。
func (b *Bus) Publish(e core.StreamEvent) {
b.mu.Lock()
defer b.mu.Unlock()
for id, s := range b.subs {
if s.closed || (s.filter != "" && s.filter != e.ConversationID) {
continue
}
select {
case s.ch <- e:
default:
b.dropLocked(id)
}
}
}
// dropLocked 在 b.mu 下关闭并移除订阅。重复调用无效果。
func (b *Bus) dropLocked(id int64) {
s, ok := b.subs[id]
if !ok || s.closed {
return
}
s.closed = true
delete(b.subs, id)
close(s.ch)
}