style: fix golangci-lint formatting issues

- Run gofmt on all swarm package files
- Fix unnecessary int64 conversion in metrics.go
- Break long line in leader_election.go for golines

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Zhaoyikaiii 2026-02-23 22:08:12 +08:00
parent 050f99ef0c
commit d17bc02493
11 changed files with 156 additions and 155 deletions

View file

@ -209,13 +209,13 @@ func DefaultConfig() *Config {
BindAddr: "0.0.0.0", BindAddr: "0.0.0.0",
BindPort: DefaultBindPort, BindPort: DefaultBindPort,
Discovery: DiscoveryConfig{ Discovery: DiscoveryConfig{
JoinAddrs: nil, JoinAddrs: nil,
GossipInterval: Duration{DefaultGossipInterval}, GossipInterval: Duration{DefaultGossipInterval},
PushPullInterval: Duration{DefaultPushPullInterval}, PushPullInterval: Duration{DefaultPushPullInterval},
NodeTimeout: Duration{DefaultNodeTimeout}, NodeTimeout: Duration{DefaultNodeTimeout},
DeadNodeTimeout: Duration{DefaultDeadNodeTimeout}, DeadNodeTimeout: Duration{DefaultDeadNodeTimeout},
AuthSecret: "", AuthSecret: "",
RequireAuth: false, RequireAuth: false,
EnableMessageSigning: false, EnableMessageSigning: false,
}, },
Handoff: HandoffConfig{ Handoff: HandoffConfig{
@ -230,16 +230,16 @@ func DefaultConfig() *Config {
Timeout: Duration{10 * time.Second}, Timeout: Duration{10 * time.Second},
}, },
LoadMonitor: LoadMonitorConfig{ LoadMonitor: LoadMonitorConfig{
Enabled: true, Enabled: true,
Interval: Duration{DefaultLoadSampleInterval}, Interval: Duration{DefaultLoadSampleInterval},
SampleSize: DefaultLoadSampleSize, SampleSize: DefaultLoadSampleSize,
CPUWeight: DefaultCPUWeight, CPUWeight: DefaultCPUWeight,
MemoryWeight: DefaultMemoryWeight, MemoryWeight: DefaultMemoryWeight,
SessionWeight: DefaultSessionWeight, SessionWeight: DefaultSessionWeight,
OffloadThreshold: DefaultOffloadThreshold, OffloadThreshold: DefaultOffloadThreshold,
MaxMemoryBytes: DefaultMaxMemoryBytes, MaxMemoryBytes: DefaultMaxMemoryBytes,
MaxGoroutines: DefaultMaxGoroutines, MaxGoroutines: DefaultMaxGoroutines,
MaxSessions: DefaultMaxSessions, MaxSessions: DefaultMaxSessions,
}, },
LeaderElection: LeaderElectionConfig{ LeaderElection: LeaderElectionConfig{
Enabled: false, Enabled: false,
@ -247,9 +247,9 @@ func DefaultConfig() *Config {
LeaderHeartbeatTimeout: Duration{10 * time.Second}, LeaderHeartbeatTimeout: Duration{10 * time.Second},
}, },
Metrics: MetricsConfig{ Metrics: MetricsConfig{
Enabled: false, Enabled: false,
ExportInterval: Duration{10 * time.Second}, ExportInterval: Duration{10 * time.Second},
PrometheusEnabled: false, PrometheusEnabled: false,
PrometheusEndpoint: "/metrics", PrometheusEndpoint: "/metrics",
}, },
} }

View file

@ -31,10 +31,10 @@ type DiscoveryService struct {
rpcConn net.Listener rpcConn net.Listener
auth *AuthProvider auth *AuthProvider
mu sync.RWMutex mu sync.RWMutex
running bool running bool
stopChan chan struct{} stopChan chan struct{}
once sync.Once once sync.Once
// Sequence number for updates // Sequence number for updates
seqNum uint64 seqNum uint64
@ -281,13 +281,13 @@ func (ds *DiscoveryService) gossipLoop() {
// GossipMessage represents a gossip message. // GossipMessage represents a gossip message.
type GossipMessage struct { type GossipMessage struct {
Type string `json:"type"` // "ping", "pong", "join", "update" Type string `json:"type"` // "ping", "pong", "join", "update"
FromNode string `json:"from_node"` FromNode string `json:"from_node"`
SeqNum uint64 `json:"seq_num"` SeqNum uint64 `json:"seq_num"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`
Payload []byte `json:"payload,omitempty"` Payload []byte `json:"payload,omitempty"`
Nodes []*NodeInfo `json:"nodes,omitempty"` // For memberlist exchange Nodes []*NodeInfo `json:"nodes,omitempty"` // For memberlist exchange
AuthToken *AuthToken `json:"auth_token,omitempty"` AuthToken *AuthToken `json:"auth_token,omitempty"`
} }
// handleGossip handles an incoming gossip message. // handleGossip handles an incoming gossip message.

View file

@ -21,11 +21,11 @@ import (
type HandoffReason string type HandoffReason string
const ( const (
ReasonOverloaded HandoffReason = "overloaded" // Load is too high ReasonOverloaded HandoffReason = "overloaded" // Load is too high
ReasonNoCapability HandoffReason = "no_capability" // Missing capability ReasonNoCapability HandoffReason = "no_capability" // Missing capability
ReasonUserRequest HandoffReason = "user_request" // User explicitly requested ReasonUserRequest HandoffReason = "user_request" // User explicitly requested
ReasonNodeLeave HandoffReason = "node_leave" // Node is leaving ReasonNodeLeave HandoffReason = "node_leave" // Node is leaving
ReasonShutdown HandoffReason = "shutdown" // Graceful shutdown ReasonShutdown HandoffReason = "shutdown" // Graceful shutdown
) )
// HandoffState represents the state of a handoff operation. // HandoffState represents the state of a handoff operation.
@ -42,27 +42,27 @@ const (
// HandoffRequest represents a request to hand off a session. // HandoffRequest represents a request to hand off a session.
type HandoffRequest struct { type HandoffRequest struct {
RequestID string `json:"request_id"` RequestID string `json:"request_id"`
Reason HandoffReason `json:"reason"` Reason HandoffReason `json:"reason"`
SessionKey string `json:"session_key"` SessionKey string `json:"session_key"`
SessionMessages []SessionMessage `json:"session_messages,omitempty"` SessionMessages []SessionMessage `json:"session_messages,omitempty"`
Context map[string]any `json:"context,omitempty"` Context map[string]any `json:"context,omitempty"`
RequiredCap string `json:"required_cap,omitempty"` RequiredCap string `json:"required_cap,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"` Metadata map[string]string `json:"metadata,omitempty"`
FromNodeID string `json:"from_node_id"` FromNodeID string `json:"from_node_id"`
FromNodeAddr string `json:"from_node_addr"` FromNodeAddr string `json:"from_node_addr"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`
} }
// HandoffResponse represents the response to a handoff request. // HandoffResponse represents the response to a handoff request.
type HandoffResponse struct { type HandoffResponse struct {
RequestID string `json:"request_id"` RequestID string `json:"request_id"`
Accepted bool `json:"accepted"` Accepted bool `json:"accepted"`
NodeID string `json:"node_id"` NodeID string `json:"node_id"`
Reason string `json:"reason,omitempty"` Reason string `json:"reason,omitempty"`
SessionKey string `json:"session_key,omitempty"` // New session key on target SessionKey string `json:"session_key,omitempty"` // New session key on target
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`
State HandoffState `json:"state"` State HandoffState `json:"state"`
} }
// HandoffCoordinator coordinates handoff operations between nodes. // HandoffCoordinator coordinates handoff operations between nodes.
@ -71,9 +71,9 @@ type HandoffCoordinator struct {
membership *MembershipManager membership *MembershipManager
config HandoffConfig config HandoffConfig
pending map[string]*HandoffOperation // request_id -> operation pending map[string]*HandoffOperation // request_id -> operation
mu sync.RWMutex mu sync.RWMutex
conn *net.UDPConn conn *net.UDPConn
// Accept/reject callbacks // Accept/reject callbacks
onHandoffRequest func(*HandoffRequest) *HandoffResponse onHandoffRequest func(*HandoffRequest) *HandoffResponse
@ -82,22 +82,22 @@ type HandoffCoordinator struct {
// HandoffOperation represents an ongoing handoff operation. // HandoffOperation represents an ongoing handoff operation.
type HandoffOperation struct { type HandoffOperation struct {
Request *HandoffRequest Request *HandoffRequest
Response *HandoffResponse Response *HandoffResponse
State HandoffState State HandoffState
StartTime time.Time StartTime time.Time
LastUpdate time.Time LastUpdate time.Time
RetryCount int RetryCount int
TargetNode *NodeWithState TargetNode *NodeWithState
} }
// NewHandoffCoordinator creates a new handoff coordinator. // NewHandoffCoordinator creates a new handoff coordinator.
func NewHandoffCoordinator(ds *DiscoveryService, config HandoffConfig) (*HandoffCoordinator, error) { func NewHandoffCoordinator(ds *DiscoveryService, config HandoffConfig) (*HandoffCoordinator, error) {
hc := &HandoffCoordinator{ hc := &HandoffCoordinator{
discovery: ds, discovery: ds,
membership: ds.membership, membership: ds.membership,
config: config, config: config,
pending: make(map[string]*HandoffOperation), pending: make(map[string]*HandoffOperation),
} }
// Bind UDP socket for handoff messages // Bind UDP socket for handoff messages

View file

@ -178,7 +178,8 @@ func (le *LeaderElection) monitorLeader() {
// Check if leader is still in the membership // Check if leader is still in the membership
if _, exists := le.membership.GetNode(leaderID); !exists { if _, exists := le.membership.GetNode(leaderID); !exists {
logger.WarnCF("swarm", "Leader no longer in membership, triggering reelection", map[string]any{"leader_id": leaderID}) logger.WarnCF("swarm", "Leader no longer in membership, triggering reelection",
map[string]any{"leader_id": leaderID})
// Trigger reelection by clearing current leader // Trigger reelection by clearing current leader
le.mu.Lock() le.mu.Lock()
le.currentLeader = "" le.currentLeader = ""

View file

@ -14,9 +14,9 @@ import (
// LoadMonitor monitors system load and calculates a load score. // LoadMonitor monitors system load and calculates a load score.
type LoadMonitor struct { type LoadMonitor struct {
config *LoadMonitorConfig config *LoadMonitorConfig
samples []float64 samples []float64
mu sync.RWMutex mu sync.RWMutex
sessionCount int sessionCount int
ticker *time.Ticker ticker *time.Ticker
stopChan chan struct{} stopChan chan struct{}
@ -33,9 +33,9 @@ func NewLoadMonitor(config *LoadMonitorConfig) *LoadMonitor {
} }
lm := &LoadMonitor{ lm := &LoadMonitor{
config: config, config: config,
samples: make([]float64, 0, config.SampleSize), samples: make([]float64, 0, config.SampleSize),
stopChan: make(chan struct{}), stopChan: make(chan struct{}),
onThreshold: make([]func(float64), 0), onThreshold: make([]func(float64), 0),
} }
return lm return lm
@ -89,20 +89,20 @@ func (lm *LoadMonitor) run() {
// LoadMetrics represents current load metrics. // LoadMetrics represents current load metrics.
type LoadMetrics struct { type LoadMetrics struct {
CPUUsage float64 `json:"cpu_usage"` CPUUsage float64 `json:"cpu_usage"`
MemoryUsage float64 `json:"memory_usage"` MemoryUsage float64 `json:"memory_usage"`
ActiveSessions int `json:"active_sessions"` ActiveSessions int `json:"active_sessions"`
Goroutines int `json:"goroutines"` Goroutines int `json:"goroutines"`
Score float64 `json:"score"` Score float64 `json:"score"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`
} }
// GetCurrentLoad returns the current load metrics. // GetCurrentLoad returns the current load metrics.
func (lm *LoadMonitor) GetCurrentLoad() *LoadMetrics { func (lm *LoadMonitor) GetCurrentLoad() *LoadMetrics {
metrics := &LoadMetrics{ metrics := &LoadMetrics{
ActiveSessions: lm.GetSessionCount(), ActiveSessions: lm.GetSessionCount(),
Goroutines: runtime.NumGoroutine(), Goroutines: runtime.NumGoroutine(),
Timestamp: time.Now().UnixNano(), Timestamp: time.Now().UnixNano(),
} }
// Get memory usage // Get memory usage
@ -245,7 +245,7 @@ func (lm *LoadMonitor) GetTrend() string {
// Simple linear regression to detect trend // Simple linear regression to detect trend
n := float64(len(lm.samples)) n := float64(len(lm.samples))
sumX := n*(n-1)/2 sumX := n * (n - 1) / 2
sumY := 0.0 sumY := 0.0
sumXY := 0.0 sumXY := 0.0
@ -255,7 +255,7 @@ func (lm *LoadMonitor) GetTrend() string {
sumXY += x * s sumXY += x * s
} }
slope := (n*sumXY - sumX*sumY) / (n*(n-1)*(2*n-1)/6) slope := (n*sumXY - sumX*sumY) / (n * (n - 1) * (2*n - 1) / 6)
if slope > TrendIncreasingThreshold { if slope > TrendIncreasingThreshold {
return "increasing" return "increasing"

View file

@ -20,9 +20,9 @@ type MembershipManager struct {
mu sync.RWMutex mu sync.RWMutex
// Event callbacks // Event callbacks
onJoin []func(*NodeInfo) onJoin []func(*NodeInfo)
onLeave []func(*NodeInfo) onLeave []func(*NodeInfo)
onUpdate []func(*NodeInfo) onUpdate []func(*NodeInfo)
} }
// NewMembershipManager creates a new membership manager. // NewMembershipManager creates a new membership manager.

View file

@ -19,13 +19,13 @@ type MetricsCollector struct {
mu sync.RWMutex mu sync.RWMutex
// Counters (atomic for performance) // Counters (atomic for performance)
messagesSent atomic.Int64 messagesSent atomic.Int64
messagesReceived atomic.Int64 messagesReceived atomic.Int64
handoffsInitiated atomic.Int64 handoffsInitiated atomic.Int64
handoffsAccepted atomic.Int64 handoffsAccepted atomic.Int64
handoffsRejected atomic.Int64 handoffsRejected atomic.Int64
handoffsFailed atomic.Int64 handoffsFailed atomic.Int64
electionsWon atomic.Int64 electionsWon atomic.Int64
// Gauges (use atomic.Value for float64) // Gauges (use atomic.Value for float64)
currentLoadScore atomic.Value // float64 currentLoadScore atomic.Value // float64
@ -40,9 +40,9 @@ type MetricsCollector struct {
// LatencyBucket tracks latency distribution. // LatencyBucket tracks latency distribution.
type LatencyBucket struct { type LatencyBucket struct {
mu sync.RWMutex mu sync.RWMutex
count int64 count int64
sum int64 sum int64
buckets [12]int64 // 0-1ms, 1-2ms, 2-5ms, 5-10ms, 10-20ms, 20-50ms, 50-100ms, 100-200ms, 200-500ms, 500ms-1s, 1-2s, 2s+ buckets [12]int64 // 0-1ms, 1-2ms, 2-5ms, 5-10ms, 10-20ms, 20-50ms, 50-100ms, 100-200ms, 200-500ms, 500ms-1s, 1-2s, 2s+
} }
@ -118,7 +118,7 @@ func (m *MetricsCollector) RecordLatency(operation string, latency time.Duration
bucket := m.latencyBuckets[operation] bucket := m.latencyBuckets[operation]
m.mu.Unlock() m.mu.Unlock()
ms := int64(latency.Milliseconds()) ms := latency.Milliseconds()
bucket.mu.Lock() bucket.mu.Lock()
bucket.count++ bucket.count++
@ -174,24 +174,24 @@ func (m *MetricsCollector) GetMetrics() map[string]any {
return map[string]any{ return map[string]any{
// Counters // Counters
"messages_sent": m.messagesSent.Load(), "messages_sent": m.messagesSent.Load(),
"messages_received": m.messagesReceived.Load(), "messages_received": m.messagesReceived.Load(),
"handoffs_initiated": m.handoffsInitiated.Load(), "handoffs_initiated": m.handoffsInitiated.Load(),
"handoffs_accepted": m.handoffsAccepted.Load(), "handoffs_accepted": m.handoffsAccepted.Load(),
"handoffs_rejected": m.handoffsRejected.Load(), "handoffs_rejected": m.handoffsRejected.Load(),
"handoffs_failed": m.handoffsFailed.Load(), "handoffs_failed": m.handoffsFailed.Load(),
"elections_won": m.electionsWon.Load(), "elections_won": m.electionsWon.Load(),
// Gauges // Gauges
"load_score": m.currentLoadScore.Load(), "load_score": m.currentLoadScore.Load(),
"active_sessions": m.activeSessions.Load(), "active_sessions": m.activeSessions.Load(),
"member_count": m.memberCount.Load(), "member_count": m.memberCount.Load(),
// System info // System info
"uptime_seconds": time.Since(m.startTime).Seconds(), "uptime_seconds": time.Since(m.startTime).Seconds(),
// Latency histograms // Latency histograms
"latency_ms": latency, "latency_ms": latency,
} }
} }
@ -205,7 +205,7 @@ func (m *MetricsCollector) percentile(bucket *LatencyBucket, p float64) float64
cumulative := int64(0) cumulative := int64(0)
// Upper bounds for each bucket in ms // Upper bounds for each bucket in ms
upperBounds := []int64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 1<<62} upperBounds := []int64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 1 << 62}
for i, count := range bucket.buckets { for i, count := range bucket.buckets {
cumulative += count cumulative += count

View file

@ -63,13 +63,13 @@ const (
// NodeState represents the state of a node in the membership view. // NodeState represents the state of a node in the membership view.
type NodeState struct { type NodeState struct {
Node *NodeInfo `json:"node"` Node *NodeInfo `json:"node"`
Status NodeStatus `json:"status"` Status NodeStatus `json:"status"`
StatusSince int64 `json:"status_since"` // Unix nano when status was set StatusSince int64 `json:"status_since"` // Unix nano when status was set
LastSeen int64 `json:"last_seen"` // Unix nano of last sighting LastSeen int64 `json:"last_seen"` // Unix nano of last sighting
LastPing int64 `json:"last_ping"` // Unix nano of last successful ping LastPing int64 `json:"last_ping"` // Unix nano of last successful ping
PingSuccess int `json:"ping_success"` // Consecutive successful pings PingSuccess int `json:"ping_success"` // Consecutive successful pings
PingFailure int `json:"ping_failure"` // Consecutive failed pings PingFailure int `json:"ping_failure"` // Consecutive failed pings
} }
// IsAvailable returns true if the node is available for handoff. // IsAvailable returns true if the node is available for handoff.
@ -85,9 +85,9 @@ func (ns *NodeState) UpdateStatus(status NodeStatus) {
// NodeEvent represents a node state change event. // NodeEvent represents a node state change event.
type NodeEvent struct { type NodeEvent struct {
Node *NodeInfo `json:"node"` Node *NodeInfo `json:"node"`
Event EventType `json:"event"` Event EventType `json:"event"`
Time int64 `json:"time"` Time int64 `json:"time"`
} }
// EventType represents the type of node event. // EventType represents the type of node event.
@ -195,13 +195,13 @@ func (ed *EventDispatcher) DispatchContext(event *NodeEvent, ctx context.Context
// NodeStats tracks statistics about a node. // NodeStats tracks statistics about a node.
type NodeStats struct { type NodeStats struct {
MessagesSent int64 `json:"messages_sent"` MessagesSent int64 `json:"messages_sent"`
MessagesReceived int64 `json:"messages_received"` MessagesReceived int64 `json:"messages_received"`
HandoffsAccepted int `json:"handoffs_accepted"` HandoffsAccepted int `json:"handoffs_accepted"`
HandoffsInitiated int `json:"handoffs_initiated"` HandoffsInitiated int `json:"handoffs_initiated"`
LastError string `json:"last_error,omitempty"` LastError string `json:"last_error,omitempty"`
LastErrorTime time.Time `json:"last_error_time,omitempty"` LastErrorTime time.Time `json:"last_error_time,omitempty"`
UptimeStart time.Time `json:"uptime_start"` UptimeStart time.Time `json:"uptime_start"`
} }
// NodeWithState combines a node with its state and stats. // NodeWithState combines a node with its state and stats.
@ -222,9 +222,9 @@ func (nws *NodeWithState) IsAvailable() bool {
// ClusterView represents the current view of the cluster. // ClusterView represents the current view of the cluster.
type ClusterView struct { type ClusterView struct {
Nodes map[string]*NodeWithState `json:"nodes"` Nodes map[string]*NodeWithState `json:"nodes"`
LocalNodeID string `json:"local_node_id"` LocalNodeID string `json:"local_node_id"`
Size int `json:"size"` Size int `json:"size"`
Version int64 `json:"version"` // View version for conflict detection Version int64 `json:"version"` // View version for conflict detection
mu sync.RWMutex mu sync.RWMutex
} }

View file

@ -17,12 +17,12 @@ import (
// SessionTransfer handles session migration between nodes. // SessionTransfer handles session migration between nodes.
type SessionTransfer struct { type SessionTransfer struct {
config RPCConfig config RPCConfig
localNode *NodeInfo localNode *NodeInfo
transfers map[string]*TransferOperation // session_key -> operation transfers map[string]*TransferOperation // session_key -> operation
mu sync.RWMutex mu sync.RWMutex
conn *net.UDPConn conn *net.UDPConn
onReceive func(*TransferPayload) onReceive func(*TransferPayload)
} }
// TransferOperation represents an ongoing transfer operation. // TransferOperation represents an ongoing transfer operation.
@ -49,15 +49,15 @@ const (
// TransferPayload represents the session data being transferred. // TransferPayload represents the session data being transferred.
type TransferPayload struct { type TransferPayload struct {
SessionKey string `json:"session_key"` SessionKey string `json:"session_key"`
SourceNodeID string `json:"source_node_id"` SourceNodeID string `json:"source_node_id"`
TargetNodeID string `json:"target_node_id"` TargetNodeID string `json:"target_node_id"`
Messages []SessionMessage `json:"messages"` Messages []SessionMessage `json:"messages"`
Summary string `json:"summary,omitempty"` Summary string `json:"summary,omitempty"`
Context map[string]any `json:"context,omitempty"` Context map[string]any `json:"context,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"` Metadata map[string]string `json:"metadata,omitempty"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`
TransferID string `json:"transfer_id"` TransferID string `json:"transfer_id"`
} }
// NewSessionTransfer creates a new session transfer handler. // NewSessionTransfer creates a new session transfer handler.

View file

@ -85,11 +85,11 @@ func TestClusterView(t *testing.T) {
func TestLoadMonitor(t *testing.T) { func TestLoadMonitor(t *testing.T) {
config := &LoadMonitorConfig{ config := &LoadMonitorConfig{
Enabled: true, Enabled: true,
Interval: Duration{time.Second}, Interval: Duration{time.Second},
SampleSize: 10, SampleSize: 10,
CPUWeight: 0.3, CPUWeight: 0.3,
MemoryWeight: 0.3, MemoryWeight: 0.3,
SessionWeight: 0.4, SessionWeight: 0.4,
} }

View file

@ -9,17 +9,17 @@ package swarm
// SessionMessage represents a message in a session. // SessionMessage represents a message in a session.
// This is shared across handoff and session transfer. // This is shared across handoff and session transfer.
type SessionMessage struct { type SessionMessage struct {
Role string `json:"role"` Role string `json:"role"`
Content string `json:"content"` Content string `json:"content"`
Timestamp int64 `json:"timestamp,omitempty"` Timestamp int64 `json:"timestamp,omitempty"`
ToolCalls []ToolCallData `json:"tool_calls,omitempty"` ToolCalls []ToolCallData `json:"tool_calls,omitempty"`
} }
// ToolCallData represents tool call information in a message. // ToolCallData represents tool call information in a message.
type ToolCallData struct { type ToolCallData struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Arguments map[string]any `json:"arguments"` Arguments map[string]any `json:"arguments"`
Result string `json:"result,omitempty"` Result string `json:"result,omitempty"`
Extra map[string]any `json:"extra,omitempty"` Extra map[string]any `json:"extra,omitempty"`
} }