Files
synapse/internal/routing/router.go
2026-06-28 03:24:26 +08:00

452 lines
12 KiB
Go

package routing
import (
"context"
"encoding/json"
"fmt"
"log"
"strings"
"sync"
"synapse/internal/agent/group"
"synapse/internal/agent/p2p"
"synapse/internal/hil"
"synapse/pkg/types"
)
// =============================================================================
// Message Router
// =============================================================================
// Router handles routing of Synapse messages
type Router struct {
mu sync.RWMutex
// Protocols
p2pProtocol *p2p.P2PProtocol
groupProtocol *group.GroupProtocol
hilProtocol *hil.HILProtocol
// External routing
externalRoutes map[string]string // system_id -> gateway endpoint
// Configuration
config *RouterConfig
}
// RouterConfig holds router configuration
type RouterConfig struct {
MaxHops int
EnableCaching bool
}
// DefaultRouterConfig returns default router configuration
func DefaultRouterConfig() *RouterConfig {
return &RouterConfig{
MaxHops: 10,
EnableCaching: true,
}
}
// =============================================================================
// NewRouter creates a new message router
// =============================================================================
func NewRouter(config *RouterConfig) *Router {
if config == nil {
config = DefaultRouterConfig()
}
return &Router{
externalRoutes: make(map[string]string),
config: config,
}
}
// SetP2PProtocol sets the P2P protocol handler
func (r *Router) SetP2PProtocol(p *p2p.P2PProtocol) {
r.mu.Lock()
defer r.mu.Unlock()
r.p2pProtocol = p
}
// SetGroupProtocol sets the Group protocol handler
func (r *Router) SetGroupProtocol(g *group.GroupProtocol) {
r.mu.Lock()
defer r.mu.Unlock()
r.groupProtocol = g
}
// SetHILProtocol sets the HiL protocol handler
func (r *Router) SetHILProtocol(h *hil.HILProtocol) {
r.mu.Lock()
defer r.mu.Unlock()
r.hilProtocol = h
}
// RegisterExternalRoute registers an external system gateway
func (r *Router) RegisterExternalRoute(systemID, endpoint string) {
r.mu.Lock()
defer r.mu.Unlock()
r.externalRoutes[systemID] = endpoint
}
// =============================================================================
// RouteMessage routes an incoming message to the appropriate handler
// =============================================================================
func (r *Router) RouteMessage(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error) {
r.mu.RLock()
defer r.mu.RUnlock()
// Validate message
if err := r.validateMessage(msg); err != nil {
return nil, err
}
// Route based on message category
if isGroupMessage(msg.Type) {
if r.groupProtocol == nil {
return nil, fmt.Errorf("group protocol not configured")
}
return r.groupProtocol.HandleMessage(ctx, msg)
}
if isP2PMessage(msg.Type) {
if r.p2pProtocol == nil {
return nil, fmt.Errorf("p2p protocol not configured")
}
return r.p2pProtocol.HandleMessage(ctx, msg)
}
return nil, fmt.Errorf("unknown message type: %s", msg.Type)
}
// =============================================================================
// SendMessage sends a message to a destination
// =============================================================================
// MessageSender is the interface for sending messages
type MessageSender interface {
Send(ctx context.Context, endpoint string, msg *types.UnifiedSynapseMessage) error
}
// TransportSender is used by the router to send messages
var _ MessageSender = (*TransportSender)(nil)
// TransportSender is a placeholder for actual transport implementation
type TransportSender struct {
httpClient *HTTPClient
}
// HTTPClient is a simple HTTP client for sending messages
type HTTPClient struct {
baseURL string
timeoutMs int
}
// Send sends a message via HTTP
func (t *TransportSender) Send(ctx context.Context, endpoint string, msg *types.UnifiedSynapseMessage) error {
if endpoint == "" {
return fmt.Errorf("endpoint is required")
}
_, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("failed to marshal message: %w", err)
}
// In a real implementation, this would use net/http to send the message
log.Printf("[Router] Sending message to %s: type=%s, id=%s", endpoint, msg.Type, msg.ID)
return nil // Placeholder - actual HTTP call would go here
}
// =============================================================================
// CreateOutboundMessage creates a message for sending to a remote agent
// =============================================================================
func (r *Router) CreateOutboundMessage(
msgType string,
sender types.AgentEndpoint,
receiver *types.AgentEndpoint,
payload any,
correlationID string,
) (*types.UnifiedSynapseMessage, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal payload: %w", err)
}
msg := &types.UnifiedSynapseMessage{
ID: types.GenerateMessageID(),
Version: "1.0.0",
Type: msgType,
Sender: sender,
Receiver: receiver,
CorrelationID: correlationID,
Payload: payloadBytes,
Timestamp: types.NowTimestamp(),
}
return msg, nil
}
// =============================================================================
// CreateTaskRequest creates a task request message
// =============================================================================
func (r *Router) CreateTaskRequest(
sender types.AgentEndpoint,
receiver types.AgentEndpoint,
taskID string,
taskType string,
taskPayload map[string]interface{},
priority types.Priority,
correlationID string,
) (*types.UnifiedSynapseMessage, error) {
payload := types.TaskRequestPayload{}
payload.Task.ID = taskID
payload.Task.Type = taskType
payload.Task.Priority = priority
payload.Task.Payload = taskPayload
return r.CreateOutboundMessage(
string(types.P2PTaskRequest),
sender,
&receiver,
payload,
correlationID,
)
}
// =============================================================================
// CreateTaskNeedHuman creates a need human message
// =============================================================================
func (r *Router) CreateTaskNeedHuman(
sender types.AgentEndpoint,
receiver types.AgentEndpoint,
taskID string,
reasonCode, reasonMessage string,
context struct {
TaskSummary string
CurrentProgress int
BlockedReason string
RelevantHistory []any
},
options []struct {
Field string
Value any
ProsCons string
Confidence int
},
suggestedValue any,
escalationLevel int,
correlationID string,
) (*types.UnifiedSynapseMessage, error) {
payload := types.TaskNeedHumanPayload{}
payload.TaskID = taskID
payload.Reason.Code = reasonCode
payload.Reason.Message = reasonMessage
payload.Context.TaskSummary = context.TaskSummary
payload.Context.CurrentProgress = context.CurrentProgress
payload.Context.BlockedReason = context.BlockedReason
payload.Context.RelevantHistory = context.RelevantHistory
// Copy options
payload.Options = make([]struct {
Field string `json:"field"`
Value any `json:"value"`
ProsCons string `json:"pros_cons,omitempty"`
Confidence int `json:"confidence,omitempty"`
}, len(options))
for i, opt := range options {
payload.Options[i].Field = opt.Field
payload.Options[i].Value = opt.Value
payload.Options[i].ProsCons = opt.ProsCons
payload.Options[i].Confidence = opt.Confidence
}
payload.SuggestedValue = suggestedValue
payload.EscalationLevel = escalationLevel
return r.CreateOutboundMessage(
string(types.P2PTaskNeedHuman),
sender,
&receiver,
payload,
correlationID,
)
}
// =============================================================================
// CreateGroupTaskSplit creates a group task split message
// =============================================================================
func (r *Router) CreateGroupTaskSplit(
sender types.AgentEndpoint,
groupID string,
originalTask struct {
ID string
Type string
Requirements map[string]interface{}
Deadline *types.Timestamp
},
subtasks []types.Subtask,
mergeStrategy types.MergeStrategy,
correlationID string,
) (*types.UnifiedSynapseMessage, error) {
payload := types.GroupTaskSplitPayload{}
payload.OriginalTask.ID = originalTask.ID
payload.OriginalTask.Type = originalTask.Type
payload.OriginalTask.Requirements = originalTask.Requirements
payload.OriginalTask.Deadline = originalTask.Deadline
payload.Subtasks = subtasks
payload.MergeStrategy = mergeStrategy
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal payload: %w", err)
}
msg := &types.UnifiedSynapseMessage{
ID: types.GenerateMessageID(),
Version: "1.0.0",
Type: string(types.GroupTaskSplit),
Sender: sender,
GroupID: groupID,
Broadcast: false,
CorrelationID: correlationID,
Payload: payloadBytes,
Timestamp: types.NowTimestamp(),
}
return msg, nil
}
// =============================================================================
// CreateGroupBroadcast creates a group broadcast message
// =============================================================================
func (r *Router) CreateGroupBroadcast(
sender types.AgentEndpoint,
groupID string,
msgType string,
payload any,
correlationID string,
) (*types.UnifiedSynapseMessage, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal payload: %w", err)
}
msg := &types.UnifiedSynapseMessage{
ID: types.GenerateMessageID(),
Version: "1.0.0",
Type: msgType,
Sender: sender,
GroupID: groupID,
Broadcast: true,
CorrelationID: correlationID,
Payload: payloadBytes,
Timestamp: types.NowTimestamp(),
}
return msg, nil
}
// =============================================================================
// CreateGroupVote creates a group vote message
// =============================================================================
func (r *Router) CreateGroupVote(
sender types.AgentEndpoint,
groupID string,
voteID string,
question string,
options []string,
voters []string,
threshold float64,
deadline types.Timestamp,
correlationID string,
) (*types.UnifiedSynapseMessage, error) {
payload := types.GroupVotePayload{}
payload.VoteID = voteID
payload.Question = question
payload.Options = options
payload.Voters = voters
payload.Threshold = threshold
payload.Deadline = deadline
payload.Status = "open"
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal payload: %w", err)
}
msg := &types.UnifiedSynapseMessage{
ID: types.GenerateMessageID(),
Version: "1.0.0",
Type: string(types.GroupVote),
Sender: sender,
GroupID: groupID,
Broadcast: true,
CorrelationID: correlationID,
Payload: payloadBytes,
Timestamp: types.NowTimestamp(),
}
return msg, nil
}
// =============================================================================
// Validation
// =============================================================================
func (r *Router) validateMessage(msg *types.UnifiedSynapseMessage) error {
if msg.ID == "" {
return types.ErrMessageFormatInvalidError
}
if msg.Sender.AgentID == "" {
return types.NewSynapseError(types.ErrMessageFormatInvalid, "Missing sender agent_id", nil)
}
if msg.Sender.Endpoint == "" {
return types.NewSynapseError(types.ErrMessageFormatInvalid, "Missing sender endpoint", nil)
}
return nil
}
// =============================================================================
// Helper Functions
// =============================================================================
func isP2PMessage(msgType string) bool {
return strings.HasPrefix(msgType, "p2p:")
}
func isGroupMessage(msgType string) bool {
return strings.HasPrefix(msgType, "group:") || strings.HasPrefix(msgType, "inter_group:")
}
// GetRoute determines the route for a message
func (r *Router) GetRoute(msg *types.UnifiedSynapseMessage) (routeType string, target string, err error) {
// P2P messages
if msg.Receiver != nil {
if msg.Receiver.SystemID != r.p2pProtocol.AgentEndpoint().SystemID {
// External system
if endpoint, ok := r.externalRoutes[msg.Receiver.SystemID]; ok {
return "external", endpoint, nil
}
return "unknown", "", fmt.Errorf("no route for system: %s", msg.Receiver.SystemID)
}
return "local", msg.Receiver.AgentID, nil
}
// Group messages
if msg.GroupID != "" {
return "group", msg.GroupID, nil
}
return "unknown", "", fmt.Errorf("no route found for message")
}