feat(orchestration/comm): agent-to-agent 通信(bus ask/reply/event,私密 reply 主题 + 超时)
This commit is contained in:
1 parent
8b723009f9
commit
20f4a91d02
4 files changed
+814
No files matched your search
@@ -0,0 +1,95 @@
|
||||
package comm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/bus"
|
||||
)
|
||||
|
||||
// AskHandler processes an Ask addressed to this agent. It runs on the bus's
|
||||
// per-subscription worker goroutine; for a single agent asks are serialized. A
|
||||
// handler should answer via (p *Probe).Reply or Answer; a non-nil returned
|
||||
// error is logged by the probe and discarded.
|
||||
type AskHandler func(ctx context.Context, a Ask) error
|
||||
|
||||
// EventHandler processes an Event addressed to, or broadcast to, this agent.
|
||||
// Because an agent subscribes to two event patterns (broadcast and addressed),
|
||||
// the bus may invoke a handler concurrently from two worker goroutines, so an
|
||||
// EventHandler must be safe for concurrent use.
|
||||
type EventHandler func(ctx context.Context, e Event) error
|
||||
|
||||
// SubscribeAsk registers h to receive every Ask addressed to this agent and
|
||||
// returns an unsubscribe function that removes the registration. When ctx is
|
||||
// canceled the registration is removed automatically; the returned function is
|
||||
// idempotent.
|
||||
func (p *Probe) SubscribeAsk(ctx context.Context, h AskHandler) (func(), error) {
|
||||
if h == nil {
|
||||
return nil, fmt.Errorf("comm: subscribe ask: handler must not be nil")
|
||||
}
|
||||
pattern := askReceivePattern(p.agentID)
|
||||
unsub, err := p.bus.Subscribe(ctx, pattern, func(bctx context.Context, e bus.Event) error {
|
||||
a, ok := e.Payload.(Ask)
|
||||
if !ok {
|
||||
return fmt.Errorf("comm: ask payload type %T, want Ask", e.Payload)
|
||||
}
|
||||
if err := h(bctx, a); err != nil {
|
||||
p.log.Warn("ask handler failed", "from", a.From, "ask_id", a.ID, "err", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("comm: subscribe ask: %w", err)
|
||||
}
|
||||
return func() { unsub() }, nil
|
||||
}
|
||||
|
||||
// SubscribeEvent registers h to receive both broadcast events and events
|
||||
// addressed to this agent. It returns an unsubscribe function that removes both
|
||||
// registrations; it is idempotent and safe to call more than once. When ctx is
|
||||
// canceled both registrations are removed automatically.
|
||||
//
|
||||
// The handler reaches the caller from two subscription worker goroutines and
|
||||
// may also run concurrently with a SubscribeAsk handler, so it must be safe for
|
||||
// concurrent use.
|
||||
func (p *Probe) SubscribeEvent(ctx context.Context, h EventHandler) (func(), error) {
|
||||
if h == nil {
|
||||
return nil, fmt.Errorf("comm: subscribe event: handler must not be nil")
|
||||
}
|
||||
|
||||
wrap := func(bctx context.Context, e bus.Event) error {
|
||||
ev, ok := e.Payload.(Event)
|
||||
if !ok {
|
||||
return fmt.Errorf("comm: event payload type %T, want Event", e.Payload)
|
||||
}
|
||||
if err := h(bctx, ev); err != nil {
|
||||
p.log.Warn("event handler failed", "from", ev.From, "name", ev.Name, "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
unsubs []func()
|
||||
)
|
||||
combined := func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
for _, u := range unsubs {
|
||||
u()
|
||||
}
|
||||
}
|
||||
|
||||
for _, pattern := range eventReceivePatterns(p.agentID) {
|
||||
u, err := p.bus.Subscribe(ctx, pattern, wrap)
|
||||
if err != nil {
|
||||
combined()
|
||||
return nil, fmt.Errorf("comm: subscribe event %q: %w", pattern, err)
|
||||
}
|
||||
mu.Lock()
|
||||
unsubs = append(unsubs, u)
|
||||
mu.Unlock()
|
||||
}
|
||||
return combined, nil
|
||||
}
|
||||
Reference in new issue
Block a user