Files
synapse/internal/api/grpcapi/server.go
2026-06-28 03:24:26 +08:00

862 lines
24 KiB
Go

package grpcapi
import (
"context"
"fmt"
"io"
"log"
"net"
"net/http"
"sync"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
"synapse/pkg/types"
)
// =============================================================================
// gRPC Server Implementation
// =============================================================================
// GRPCServer wraps the gRPC server and gateway
type GRPCServer struct {
grpcServer *grpc.Server
gateway *http.Server
mu sync.RWMutex
// Services
taskService *TaskServiceImpl
messageService *MessageServiceImpl
capabilitiesService *CapabilitiesServiceImpl
groupService *GroupServiceImpl
healthService *HealthServiceImpl
// Dependencies
taskExecutor TaskExecutor
msgRouter MessageRouter
capsProvider CapabilitiesProvider
groupManager GroupManager
}
// TaskExecutor interface for task operations
type TaskExecutor interface {
SubmitTask(ctx context.Context, taskType string, payload map[string]any, externalTaskID, priority, callbackURL, callbackToken string) (string, string, error)
GetTaskStatus(ctx context.Context, taskID string) (*types.TaskInfo, error)
ListTasks(ctx context.Context, status string, limit, offset int) ([]types.TaskInfo, int, error)
CancelTask(ctx context.Context, taskID string) error
}
type MessageRouter interface {
RouteMessage(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error)
}
type CapabilitiesProvider interface {
GetCapabilities(ctx context.Context) (*types.AgentCapabilities, error)
}
type GroupManager interface {
CreateGroup(ctx context.Context, name, description, coordinatorID string, initialMembers []string) (string, error)
JoinGroup(ctx context.Context, groupID string, agent *types.AgentEndpoint) error
LeaveGroup(ctx context.Context, groupID, agentID string) error
DisbandGroup(ctx context.Context, groupID string) error
ListMembers(ctx context.Context, groupID string) ([]types.GroupMember, error)
// Voting operations
InitiateVote(ctx context.Context, groupID, initiatorID, question string, options []string, threshold float64, timeoutSeconds int32) (string, error)
SubmitVote(ctx context.Context, voteID, agentID, option, reason string) error
GetVoteStatus(ctx context.Context, voteID string) (*VoteStatus, error)
// Task split and merge
SplitTask(ctx context.Context, groupID, taskID, taskType, requirements string, strategy SplitStrategy) ([]*SubtaskInfo, error)
MergeResults(ctx context.Context, groupID, parentTaskID string, strategy MergeStrategy) (*anypb.Any, string, error)
}
// VoteStatus represents the status of a vote
type VoteStatus struct {
VoteID string
Status string // open, decided, expired
Question string
TotalVotes int
RequiredVotes int
WinningOption string
Votes map[string]string // agentID -> option
}
// =============================================================================
// Server Constructor
// =============================================================================
// NewGRPCServer creates a new gRPC server
func NewGRPCServer(taskExec TaskExecutor, msgRouter MessageRouter, capsProvider CapabilitiesProvider, groupManager GroupManager) *GRPCServer {
return &GRPCServer{
taskExecutor: taskExec,
msgRouter: msgRouter,
capsProvider: capsProvider,
groupManager: groupManager,
taskService: &TaskServiceImpl{},
messageService: &MessageServiceImpl{},
capabilitiesService: &CapabilitiesServiceImpl{},
groupService: &GroupServiceImpl{},
healthService: &HealthServiceImpl{},
}
}
// Start starts both gRPC and gateway servers
func (s *GRPCServer) Start(grpcPort, gatewayPort int) error {
// Start gRPC server
go func() {
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", grpcPort))
if err != nil {
log.Fatalf("[gRPC] Failed to listen: %v", err)
}
// Create gRPC server with interceptors
opts := []grpc.ServerOption{
grpc.UnaryInterceptor(s.unaryInterceptor),
grpc.StreamInterceptor(s.streamInterceptor),
}
s.grpcServer = grpc.NewServer(opts...)
// Register services
// Note: These would be generated from proto
// RegisterTaskServiceServer(s.grpcServer, s.taskService)
// RegisterA2AMessageServiceServer(s.grpcServer, s.messageService)
// ... etc
log.Printf("[gRPC] Server started on port %d", grpcPort)
if err := s.grpcServer.Serve(lis); err != nil {
log.Fatalf("[gRPC] Failed to serve: %v", err)
}
}()
// Start gRPC-Gateway
go func() {
_ = context.Background() // ctx reserved for future gateway handler registration
mux := http.NewServeMux()
// Register gateway handlers
// Note: These would be generated from proto by grpc-gateway
// err := RegisterTaskServiceHandlerFromEndpoint(ctx, mux, fmt.Sprintf("localhost:%d", grpcPort), opts)
// ... etc
s.gateway = &http.Server{
Addr: fmt.Sprintf(":%d", gatewayPort),
Handler: mux,
}
log.Printf("[Gateway] HTTP server started on port %d", gatewayPort)
if err := s.gateway.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("[Gateway] Failed to serve: %v", err)
}
}()
return nil
}
// Stop gracefully stops both servers
func (s *GRPCServer) Stop() {
if s.grpcServer != nil {
s.grpcServer.GracefulStop()
}
if s.gateway != nil {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
s.gateway.Shutdown(ctx)
}
}
// =============================================================================
// Interceptors
// =============================================================================
func (s *GRPCServer) unaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
// Extract metadata
md, ok := metadata.FromIncomingContext(ctx)
if ok {
// Check API key
apiKeys := md.Get("x-api-key")
if len(apiKeys) > 0 {
// Validate API key
log.Printf("[gRPC] API Key: %s", apiKeys[0])
}
}
// Call handler
return handler(ctx, req)
}
func (s *GRPCServer) streamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
// Extract metadata
md, ok := metadata.FromIncomingContext(ss.Context())
if ok {
apiKeys := md.Get("x-api-key")
if len(apiKeys) > 0 {
log.Printf("[gRPC Stream] API Key: %s", apiKeys[0])
}
}
return handler(srv, ss)
}
// =============================================================================
// Task Service Implementation
// =============================================================================
type TaskServiceImpl struct {
UnimplementedTaskServiceServer
taskExecutor TaskExecutor
mu sync.RWMutex
streamClients map[string]chan *TaskStatusUpdate
}
func (s *TaskServiceImpl) SubmitTask(ctx context.Context, req *SubmitTaskRequest) (*SubmitTaskResponse, error) {
// Convert payload
payload := make(map[string]any)
if req.Payload != nil {
// Unpack Any to map
// This would use protojson or similar
}
taskID, status, err := s.taskExecutor.SubmitTask(
ctx,
req.TaskType,
payload,
req.ExternalTaskId,
req.Priority,
req.CallbackUrl,
req.CallbackToken,
)
if err != nil {
return &SubmitTaskResponse{
Success: false,
ErrorCode: "TASK_SUBMISSION_FAILED",
Message: err.Error(),
}, nil
}
return &SubmitTaskResponse{
Success: true,
TaskId: taskID,
Status: parseTaskStatus(status),
Message: "任务已接收",
}, nil
}
func (s *TaskServiceImpl) SubmitTaskWithFiles(ctx context.Context, req *SubmitTaskWithFilesRequest) (*SubmitTaskResponse, error) {
// Handle file uploads
// Convert files and call SubmitTask
return s.SubmitTask(ctx, req.Request)
}
func (s *TaskServiceImpl) GetTask(ctx context.Context, req *GetTaskRequest) (*TaskStatusResponse, error) {
task, err := s.taskExecutor.GetTaskStatus(ctx, req.TaskId)
if err != nil {
return &TaskStatusResponse{
Success: false,
ErrorCode: "TASK_NOT_FOUND",
ErrorMessage: err.Error(),
}, nil
}
return &TaskStatusResponse{
Success: true,
Task: convertTaskInfo(task),
}, nil
}
func (s *TaskServiceImpl) GetTaskStatus(ctx context.Context, req *GetTaskRequest) (*TaskStatusResponse, error) {
return s.GetTask(ctx, req)
}
func (s *TaskServiceImpl) ListTasks(ctx context.Context, req *ListTasksRequest) (*ListTasksResponse, error) {
tasks, total, err := s.taskExecutor.ListTasks(ctx, req.Status, int(req.Limit), int(req.Offset))
if err != nil {
return &ListTasksResponse{
Success: false,
}, err
}
var protoTasks []*TaskInfo
for _, t := range tasks {
protoTasks = append(protoTasks, convertTaskInfo(&t))
}
return &ListTasksResponse{
Success: true,
Tasks: protoTasks,
Total: int32(total),
Limit: req.Limit,
Offset: req.Offset,
HasMore: int(req.Offset+req.Limit) < total,
}, nil
}
func (s *TaskServiceImpl) CancelTask(ctx context.Context, req *CancelTaskRequest) (*CancelTaskResponse, error) {
if err := s.taskExecutor.CancelTask(ctx, req.TaskId); err != nil {
return &CancelTaskResponse{
Success: false,
Message: err.Error(),
}, nil
}
return &CancelTaskResponse{
Success: true,
TaskId: req.TaskId,
Status: TaskStatus_TASK_STATUS_CANCELLED,
Message: "任务已取消",
}, nil
}
// StreamTaskStatus implements bidirectional streaming for task status updates
func (s *TaskServiceImpl) StreamTaskStatus(stream TaskService_StreamTaskStatusServer) error {
// Register client
clientID := fmt.Sprintf("stream-%d", time.Now().UnixNano())
statusChan := make(chan *TaskStatusUpdate, 100)
s.mu.Lock()
s.streamClients[clientID] = statusChan
s.mu.Unlock()
defer func() {
s.mu.Lock()
delete(s.streamClients, clientID)
s.mu.Unlock()
close(statusChan)
}()
// Goroutine to send status updates
go func() {
for update := range statusChan {
if err := stream.Send(update); err != nil {
log.Printf("[gRPC Stream] Send error: %v", err)
return
}
}
}()
// Receive subscription requests
for {
req, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
if req.Subscribe {
log.Printf("[gRPC Stream] Client %s subscribed to task %s", clientID, req.TaskId)
// Start watching task status
go s.watchTaskStatus(req.TaskId, statusChan)
} else {
log.Printf("[gRPC Stream] Client %s unsubscribed from task %s", clientID, req.TaskId)
}
}
}
func (s *TaskServiceImpl) watchTaskStatus(taskID string, ch chan *TaskStatusUpdate) {
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
for range ticker.C {
task, err := s.taskExecutor.GetTaskStatus(context.Background(), taskID)
if err != nil {
continue
}
ch <- &TaskStatusUpdate{
TaskId: taskID,
Status: parseTaskStatus(task.Status),
Progress: int32(task.Progress),
Message: task.Message,
}
// Stop if terminal state
if task.Status == "completed" || task.Status == "failed" || task.Status == "cancelled" {
return
}
}
}
// =============================================================================
// Message Service Implementation
// =============================================================================
type MessageServiceImpl struct {
UnimplementedA2AMessageServiceServer
msgRouter MessageRouter
}
func (s *MessageServiceImpl) SendMessage(ctx context.Context, req *SendMessageRequest) (*SendMessageResponse, error) {
// Convert proto message to internal format
msg := convertProtoToInternalMessage(req.Message)
resp, err := s.msgRouter.RouteMessage(ctx, msg)
if err != nil {
return &SendMessageResponse{
Success: false,
Error: err.Error(),
}, nil
}
return &SendMessageResponse{
Success: true,
MessageId: resp.ID,
}, nil
}
// StreamMessages implements bidirectional streaming for Synapse messages
func (s *MessageServiceImpl) StreamMessages(stream A2AMessageService_StreamMessagesServer) error {
ctx := stream.Context()
clientID := fmt.Sprintf("msg-stream-%d", time.Now().UnixNano())
log.Printf("[gRPC Stream] New message stream: %s", clientID)
// Goroutine to receive messages
go func() {
for {
msg, err := stream.Recv()
if err == io.EOF {
return
}
if err != nil {
log.Printf("[gRPC Stream] Receive error: %v", err)
return
}
internalMsg := convertProtoToInternalMessage(msg)
resp, err := s.msgRouter.RouteMessage(ctx, internalMsg)
if err != nil {
log.Printf("[gRPC Stream] Route error: %v", err)
continue
}
protoResp := convertInternalToProtoMessage(resp)
if err := stream.Send(protoResp); err != nil {
log.Printf("[gRPC Stream] Send error: %v", err)
return
}
}
}()
// Keep stream alive
<-ctx.Done()
log.Printf("[gRPC Stream] Message stream closed: %s", clientID)
return nil
}
func (s *MessageServiceImpl) ReceiveCallback(req *emptypb.Empty, stream A2AMessageService_ReceiveCallbackServer) error {
// This would be used by agents to receive callback messages via gRPC stream
// Implementation depends on callback routing mechanism
return status.Error(codes.Unimplemented, "not implemented")
}
// =============================================================================
// Capabilities Service Implementation
// =============================================================================
type CapabilitiesServiceImpl struct {
UnimplementedCapabilitiesServiceServer
provider CapabilitiesProvider
}
func (s *CapabilitiesServiceImpl) GetCapabilities(ctx context.Context, req *emptypb.Empty) (*CapabilitiesResponse, error) {
caps, err := s.provider.GetCapabilities(ctx)
if err != nil {
return &CapabilitiesResponse{
Success: false,
Error: err.Error(),
}, nil
}
return &CapabilitiesResponse{
Success: true,
Capabilities: convertAgentCapabilities(caps),
}, nil
}
func (s *CapabilitiesServiceImpl) QueryAgent(ctx context.Context, req *QueryAgentRequest) (*CapabilitiesResponse, error) {
// Query specific agent's capabilities
return s.GetCapabilities(ctx, nil)
}
func (s *CapabilitiesServiceImpl) ListAgents(ctx context.Context, req *emptypb.Empty) (*ListAgentsResponse, error) {
return &ListAgentsResponse{
Success: true,
Agents: []*AgentInfo{},
}, nil
}
// =============================================================================
// Group Service Implementation
// =============================================================================
type GroupServiceImpl struct {
UnimplementedGroupServiceServer
groupManager GroupManager
}
func (s *GroupServiceImpl) CreateGroup(ctx context.Context, req *CreateGroupRequest) (*CreateGroupResponse, error) {
groupID, err := s.groupManager.CreateGroup(ctx, req.Name, req.Description, req.CoordinatorId, req.InitialMembers)
if err != nil {
return &CreateGroupResponse{
Success: false,
Error: err.Error(),
}, nil
}
return &CreateGroupResponse{
Success: true,
GroupId: groupID,
Group: &GroupInfo{
Id: groupID,
Name: req.Name,
Description: req.Description,
CoordinatorId: req.CoordinatorId,
Status: "active",
CreatedAt: timestamppb.Now(),
},
}, nil
}
func (s *GroupServiceImpl) JoinGroup(ctx context.Context, req *JoinGroupRequest) (*JoinGroupResponse, error) {
err := s.groupManager.JoinGroup(ctx, req.GroupId, convertProtoEndpoint(req.Agent))
if err != nil {
return &JoinGroupResponse{Success: false, Error: err.Error()}, nil
}
return &JoinGroupResponse{Success: true}, nil
}
func (s *GroupServiceImpl) LeaveGroup(ctx context.Context, req *LeaveGroupRequest) (*LeaveGroupResponse, error) {
err := s.groupManager.LeaveGroup(ctx, req.GroupId, req.AgentId)
if err != nil {
return &LeaveGroupResponse{Success: false, Error: err.Error()}, nil
}
return &LeaveGroupResponse{Success: true}, nil
}
func (s *GroupServiceImpl) DisbandGroup(ctx context.Context, req *DisbandGroupRequest) (*DisbandGroupResponse, error) {
err := s.groupManager.DisbandGroup(ctx, req.GroupId)
if err != nil {
return &DisbandGroupResponse{Success: false, Error: err.Error()}, nil
}
return &DisbandGroupResponse{Success: true}, nil
}
func (s *GroupServiceImpl) ListGroupMembers(ctx context.Context, req *ListGroupMembersRequest) (*ListGroupMembersResponse, error) {
members, err := s.groupManager.ListMembers(ctx, req.GroupId)
if err != nil {
return &ListGroupMembersResponse{Success: false}, nil
}
var protoMembers []*GroupMember
for _, m := range members {
protoMembers = append(protoMembers, &GroupMember{
AgentId: m.AgentID,
AgentName: m.AgentName,
Role: m.Role,
Status: string(m.Status),
JoinedAt: timestamppb.Now(),
})
}
return &ListGroupMembersResponse{
Success: true,
Members: protoMembers,
}, nil
}
// StreamGroupMessages implements bidirectional streaming for group messages
func (s *GroupServiceImpl) StreamGroupMessages(stream GroupService_StreamGroupMessagesServer) error {
ctx := stream.Context()
clientID := fmt.Sprintf("group-stream-%d", time.Now().UnixNano())
log.Printf("[gRPC Stream] New group stream: %s", clientID)
go func() {
for {
msg, err := stream.Recv()
if err == io.EOF {
return
}
if err != nil {
log.Printf("[gRPC Stream] Receive error: %v", err)
return
}
log.Printf("[gRPC Stream] Group message received: group=%s, type=%v", msg.GroupId, msg.Type)
// Echo back for now
if err := stream.Send(msg); err != nil {
log.Printf("[gRPC Stream] Send error: %v", err)
return
}
}
}()
<-ctx.Done()
log.Printf("[gRPC Stream] Group stream closed: %s", clientID)
return nil
}
// InitiateVote initiates a voting process in the group
func (s *GroupServiceImpl) InitiateVote(ctx context.Context, req *VoteRequest) (*VoteResponse, error) {
voteID, err := s.groupManager.InitiateVote(ctx, req.GroupId, req.InitiatorId, req.Question, req.Options, req.Threshold, req.TimeoutSeconds)
if err != nil {
return &VoteResponse{
Success: false,
Error: err.Error(),
}, nil
}
return &VoteResponse{
Success: true,
VoteId: voteID,
}, nil
}
// SubmitVote submits a vote in an ongoing voting process
func (s *GroupServiceImpl) SubmitVote(ctx context.Context, req *SubmitVoteRequest) (*SubmitVoteResponse, error) {
err := s.groupManager.SubmitVote(ctx, req.VoteId, req.AgentId, req.Option, req.Reason)
if err != nil {
return &SubmitVoteResponse{
Success: false,
Error: err.Error(),
}, nil
}
return &SubmitVoteResponse{
Success: true,
}, nil
}
// GetVoteStatus gets the current status of a vote
func (s *GroupServiceImpl) GetVoteStatus(ctx context.Context, req *GetVoteStatusRequest) (*VoteStatusResponse, error) {
status, err := s.groupManager.GetVoteStatus(ctx, req.VoteId)
if err != nil {
return &VoteStatusResponse{
Success: false,
Error: err.Error(),
}, nil
}
return &VoteStatusResponse{
Success: true,
VoteId: status.VoteID,
Status: status.Status,
Question: status.Question,
TotalVotes: int32(status.TotalVotes),
RequiredVotes: int32(status.RequiredVotes),
WinningOption: status.WinningOption,
}, nil
}
// SplitTask splits a task into subtasks across group members
func (s *GroupServiceImpl) SplitTask(ctx context.Context, req *SplitTaskRequest) (*SplitTaskResponse, error) {
subtasks, err := s.groupManager.SplitTask(ctx, req.GroupId, req.TaskId, req.TaskType, req.Requirements, req.Strategy)
if err != nil {
return &SplitTaskResponse{
Success: false,
Error: err.Error(),
}, nil
}
return &SplitTaskResponse{
Success: true,
GroupId: req.GroupId,
ParentTaskId: req.TaskId,
Subtasks: subtasks,
}, nil
}
// MergeResults merges results from subtasks
func (s *GroupServiceImpl) MergeResults(ctx context.Context, req *MergeResultsRequest) (*MergeResultsResponse, error) {
mergedResult, summary, err := s.groupManager.MergeResults(ctx, req.GroupId, req.ParentTaskId, req.Strategy)
if err != nil {
return &MergeResultsResponse{
Success: false,
Error: err.Error(),
}, nil
}
return &MergeResultsResponse{
Success: true,
ParentTaskId: req.ParentTaskId,
MergedResult: mergedResult,
Summary: summary,
}, nil
}
// =============================================================================
// Health Service Implementation
// =============================================================================
type HealthServiceImpl struct {
UnimplementedHealthServiceServer
}
func (s *HealthServiceImpl) HealthCheck(ctx context.Context, req *emptypb.Empty) (*HealthCheckResponse, error) {
return &HealthCheckResponse{
Status: "healthy",
Version: "1.0.0",
UptimeSeconds: 3600,
ActiveTasks: 0,
}, nil
}
// =============================================================================
// Conversion Helpers
// =============================================================================
func parseTaskStatus(status string) TaskStatus {
switch status {
case "received":
return TaskStatus_TASK_STATUS_RECEIVED
case "processing":
return TaskStatus_TASK_STATUS_PROCESSING
case "completed":
return TaskStatus_TASK_STATUS_COMPLETED
case "failed":
return TaskStatus_TASK_STATUS_FAILED
case "cancelled":
return TaskStatus_TASK_STATUS_CANCELLED
case "rejected":
return TaskStatus_TASK_STATUS_REJECTED
case "waiting":
return TaskStatus_TASK_STATUS_WAITING
default:
return TaskStatus_TASK_STATUS_UNSPECIFIED
}
}
func convertTaskInfo(task *types.TaskInfo) *TaskInfo {
if task == nil {
return nil
}
var result *anypb.Any
if task.Result != nil {
result, _ = anypb.New(&anypb.Any{Value: []byte(fmt.Sprintf("%v", task.Result))})
}
return &TaskInfo{
Id: task.ID,
Type: task.Type,
Status: parseTaskStatus(task.Status),
Progress: int32(task.Progress),
Message: task.Message,
CreatedAt: timestamppb.Now(),
UpdatedAt: timestamppb.Now(),
Result: result,
}
}
func convertAgentCapabilities(caps *types.AgentCapabilities) *AgentCapabilities {
if caps == nil {
return nil
}
var taskTypes []*SupportedTaskType
for _, tt := range caps.SupportedTaskTypes {
taskTypes = append(taskTypes, &SupportedTaskType{
Type: tt.Type,
Name: tt.Name,
Description: tt.Description,
EstimatedTime: tt.EstimatedTime,
InputSchema: tt.InputSchema,
OutputSchema: tt.OutputSchema,
Capabilities: tt.Capabilities,
})
}
var transportModes []TransportMode
for _, tm := range caps.SupportedTransportModes {
transportModes = append(transportModes, TransportMode(TransportMode_value[string(tm)]))
}
return &AgentCapabilities{
AgentId: caps.AgentID,
AgentName: caps.AgentName,
SystemId: caps.SystemID,
ProtocolVersion: caps.ProtocolVersion,
SupportedTaskTypes: taskTypes,
SupportedContentTypes: caps.SupportedContentTypes,
SupportedTransportModes: transportModes,
MaxFileSize: caps.MaxFileSize,
MaxConcurrentTasks: int32(caps.MaxConcurrentTasks),
MaxTaskDurationSeconds: int32(caps.MaxTaskDuration),
SupportProgressUpdates: caps.SupportProgressUpdates,
SupportStreaming: caps.SupportStreaming,
SupportHil: caps.SupportHiL,
SupportGroups: caps.SupportGroups,
}
}
func convertProtoToInternalMessage(msg *A2AMessage) *types.UnifiedSynapseMessage {
if msg == nil {
return nil
}
var payload []byte
if msg.Payload != nil {
payload = msg.Payload.Value
}
return &types.UnifiedSynapseMessage{
ID: msg.Id,
Version: msg.Version,
Type: string(MessageType_name[int32(msg.Type)]),
Sender: types.AgentEndpoint{AgentID: msg.Sender.AgentId},
CorrelationID: msg.CorrelationId,
ReplyTo: msg.ReplyTo,
GroupID: msg.GroupId,
Timestamp: types.Timestamp(msg.Timestamp.AsTime().Format(time.RFC3339)),
Payload: payload,
Signature: msg.Signature,
}
}
func convertInternalToProtoMessage(msg *types.UnifiedSynapseMessage) *A2AMessage {
if msg == nil {
return nil
}
var payload *anypb.Any
if len(msg.Payload) > 0 {
payload = &anypb.Any{Value: msg.Payload}
}
return &A2AMessage{
Id: msg.ID,
Version: msg.Version,
Type: MessageType(MessageType_value[msg.Type]),
Sender: &AgentEndpoint{AgentId: msg.Sender.AgentID},
CorrelationId: msg.CorrelationID,
ReplyTo: msg.ReplyTo,
GroupId: msg.GroupID,
Timestamp: timestamppb.Now(),
Payload: payload,
Signature: msg.Signature,
}
}
func convertProtoEndpoint(endpoint *AgentEndpoint) *types.AgentEndpoint {
if endpoint == nil {
return nil
}
return &types.AgentEndpoint{
AgentID: endpoint.AgentId,
AgentName: endpoint.AgentName,
SystemID: endpoint.SystemId,
Endpoint: endpoint.Endpoint,
Version: endpoint.Version,
}
}