Files
telecom-platform/apps/carrier-connector/internal/smdp/selection_learning.go
T
NutcaseandGitHub 626a6f7349 New features (#62)
* refactor: Rename handler package to handlers in carrier-connector

- Rename internal/handler directory to internal/handlers
- Update handler import path in main.go and routes.go

* feat: Add SM-DP+ handler with multi-carrier management and profile operations

- Add SMDPHandler struct with manager field and NewSMDPHandler constructor
- Add AddCarrier endpoint with JSON binding and carrier configuration support
- Add RemoveCarrier endpoint with carrier ID parameter validation
- Add DownloadProfile endpoint with 30-second timeout and context cancellation
- Add GetCarrierStatus endpoint returning all carriers with timestamp
- Add GetProfileStatus endpoint with EID/ICCID validation

* feat: Add SM-DP+ integration layer with multi-carrier system initialization and route registration

- Add SMDPIntegration struct with service, handler, repository, and logger fields
- Add NewSMDPIntegration constructor with default carrier initialization
- Add InitializeSystem method with carrier health logging and status tracking
- Add RegisterRoutes method with profile, carrier, health, and metrics endpoints
- Add healthHandler with carrier health status aggregation and degraded state detection
- Add metricsHandler

* feat: Add SM-DP+ service layer with multi-carrier management and profile download operations

- Add SMDPService struct with manager, repository, and logger fields
- Add NewSMDPService constructor with default config and background health checking
- Add InitializeDefaultCarriers with AT&T, Verizon, and T-Mobile DE configurations
- Add DownloadProfile method with context support and request logging
- Add GetOptimalCarrier wrapping SelectCarrier for best carrier selection
- Add GetCarrierHealth returning

* feat: Add SM-DP+ manager with multi-carrier load balancing, health checking, and profile operations

- Add DefaultManagerConfig with 30s health checks, 3 retries, and circuit breaker configuration
- Add CarrierConfig struct with ES2 settings, MCC/MNC, priority, and feature support
- Add ToCarrier method converting CarrierConfig to Carrier with metrics and capabilities
- Add HealthChecker with periodic carrier health monitoring and status evaluation
- Add LoadBalancer with weighted round-robin, least

* feat: Add intelligent carrier selection service with scoring, recommendations, and analytics

- Add SelectionService struct with manager, handler, and logger fields
- Add NewSelectionService constructor with info-level logging
- Add IntelligentCarrierSelection method with comprehensive criteria evaluation
- Add IntelligentSelectionRequest with region, profile type, urgency, and cost sensitivity
- Add UserPreferences and BusinessContext structs for selection customization
- Add IntelligentSelectionResponse with selected

* feat: Add carrier selection handler with optimal selection, analytics, and performance monitoring endpoints

- Add SelectionHandler struct with manager and logger fields
- Add NewSelectionHandler constructor with info-level logging
- Add SelectOptimalCarrier endpoint with criteria validation and scoring
- Add SelectCarrier endpoint using default selection criteria
- Add GetSelectionHistory endpoint with carrier ID path parameter
- Add UpdateLearning endpoint with performance feedback validation
- Add GetSelectionAnalytics endpoint with carrier stats

* feat: Add carrier selection integration layer with multi-carrier setup, HTTP server, and demo capabilities

- Add SelectionIntegration struct with manager, selection service, handler, and SMDP service fields
- Add NewSelectionIntegration constructor with default manager config and 30s health checks
- Add SetupCarriers with AT&T, Verizon, T-Mobile DE, and Orange FR configurations
- Add StartServer method with health endpoint and 30s read/write timeouts
- Add StartHealthChecking wrapper for background

* feat: Refactor profile handlers into modular core components with simplified operations

- Split profile_handlers.go into profile_handlers_core.go, profile_handlers_management_core.go, and profile_handlers_management_delete.go
- Simplify OrderProfileHandlerWithRepo with ICCID/IMSI-only validation and "downloaded" state
- Remove webhook and message queue notifications from order handler
- Add UpdateProfileHandler with state validation for downloaded/activated/deactivated/expired
- Add DeleteProfileHandler with existence

* refactor: Extract carrier setup logic into separate file and remove unnecessary comments

- Move SetupCarriers method from selection_integration.go to new setup_carriers.go file
- Remove redundant comments from SelectionIntegration struct and methods
- Clean up inline comments in RunDemo method while preserving functionality

* feat: Add intelligent carrier selection algorithm with machine learning optimization and performance tracking

- Add SelectionAlgorithm with scoring, weighting, and ML-based carrier selection
- Add SelectionCriteria struct with region, profile type, urgency, and cost/performance weights
- Add CarrierScore with performance, reliability, cost, region, and capability scores
- Add calculateWeightedScore combining all scores with ML-optimized weights
- Add getWeights with urgency-based adjustments

* feat: Integrate SelectionAlgorithm into SMDPManager with optimal carrier selection and learning capabilities

- Add selector field to SMDPManager struct with SelectionAlgorithm type
- Initialize selector in NewSMDPManager constructor
- Add SelectOptimalCarrier method filtering healthy carriers and delegating to selection algorithm
- Add SelectCarrier wrapper with default criteria (operational profile, medium urgency, balanced weights)
- Add GetSelectionHistory method returning carrier selection history
- Add UpdateLearning
2026-05-01 23:29:35 +03:00

332 lines
9.8 KiB
Go

package smdp
import (
"math"
"time"
)
// LearningModel represents the machine learning model for carrier selection
type LearningModel struct {
// Performance tracking
carrierPerformance map[string]*PerformanceMetrics
learningRate float64
adaptationThreshold float64
// Weight optimization
baseWeights WeightVector
optimizedWeights WeightVector
// Historical data
performanceHistory map[string][]float64
selectionHistory map[string][]SelectionRecord
// Model parameters
momentum float64
regularization float64
decayRate float64
}
// PerformanceMetrics tracks carrier performance over time
type PerformanceMetrics struct {
SuccessRate float64
AverageResponseTime float64
Reliability float64
CostEfficiency float64
LastUpdated time.Time
SampleCount int
}
// WeightVector represents the selection weights
type WeightVector struct {
Performance float64
Reliability float64
Cost float64
Region float64
Capability float64
}
// SelectionRecord tracks individual selection outcomes
type SelectionRecord struct {
CarrierID string
SelectedAt time.Time
ExpectedScore float64
ActualScore float64
Performance float64
ResponseTime time.Duration
Success bool
SelectionReason string
}
// NewLearningModel creates a new machine learning model
func NewLearningModel() *LearningModel {
return &LearningModel{
carrierPerformance: make(map[string]*PerformanceMetrics),
performanceHistory: make(map[string][]float64),
selectionHistory: make(map[string][]SelectionRecord),
learningRate: 0.01,
adaptationThreshold: 0.05,
momentum: 0.9,
regularization: 0.001,
decayRate: 0.995,
baseWeights: WeightVector{
Performance: 0.3,
Reliability: 0.3,
Cost: 0.2,
Region: 0.1,
Capability: 0.1,
},
optimizedWeights: WeightVector{
Performance: 0.3,
Reliability: 0.3,
Cost: 0.2,
Region: 0.1,
Capability: 0.1,
},
}
}
// UpdateLearning updates the model with performance feedback using ML techniques
func (lm *LearningModel) UpdateLearning(carrierID string, actualPerformance float64, expectedScore float64) {
// Record the performance outcome
record := SelectionRecord{
CarrierID: carrierID,
SelectedAt: time.Now(),
ExpectedScore: expectedScore,
ActualScore: actualPerformance,
Performance: actualPerformance,
Success: actualPerformance >= 70.0, // 70% threshold for success
}
// Add to selection history
history := lm.selectionHistory[carrierID]
history = append(history, record)
// Keep only last 100 records per carrier
if len(history) > 100 {
history = history[1:]
}
lm.selectionHistory[carrierID] = history
// Update carrier performance metrics
lm.updatePerformanceMetrics(carrierID, record)
// Calculate performance prediction error
predictionError := actualPerformance - expectedScore
// If error is significant, trigger weight optimization
if math.Abs(predictionError) > lm.adaptationThreshold {
lm.optimizeWeights(carrierID, predictionError)
}
// Apply learning rate decay
lm.learningRate *= lm.decayRate
if lm.learningRate < 0.001 {
lm.learningRate = 0.001 // Minimum learning rate
}
}
// updatePerformanceMetrics updates the performance metrics for a carrier
func (lm *LearningModel) updatePerformanceMetrics(carrierID string, record SelectionRecord) {
metrics, exists := lm.carrierPerformance[carrierID]
if !exists {
metrics = &PerformanceMetrics{
LastUpdated: time.Now(),
}
lm.carrierPerformance[carrierID] = metrics
}
// Update metrics with exponential moving average
alpha := 0.1 // Smoothing factor
if metrics.SampleCount == 0 {
// First sample
metrics.SuccessRate = 1.0
if !record.Success {
metrics.SuccessRate = 0.0
}
metrics.AverageResponseTime = float64(record.ResponseTime.Milliseconds())
metrics.Reliability = record.Performance / 100.0
} else {
// Exponential moving average update
if record.Success {
metrics.SuccessRate = alpha*1.0 + (1-alpha)*metrics.SuccessRate
} else {
metrics.SuccessRate = alpha*0.0 + (1-alpha)*metrics.SuccessRate
}
responseTimeMs := float64(record.ResponseTime.Milliseconds())
metrics.AverageResponseTime = alpha*responseTimeMs + (1-alpha)*metrics.AverageResponseTime
metrics.Reliability = alpha*(record.Performance/100.0) + (1-alpha)*metrics.Reliability
}
metrics.CostEfficiency = 1.0 - (float64(record.ResponseTime.Milliseconds()) / 1000.0) // Simple cost proxy
metrics.LastUpdated = time.Now()
metrics.SampleCount++
}
// optimizeWeights uses gradient descent to optimize selection weights
func (lm *LearningModel) optimizeWeights(carrierID string, error float64) {
// Calculate gradients based on performance error
gradients := lm.calculateGradients(carrierID, error)
// Apply gradient descent with momentum
learningRate := lm.learningRate
// Update optimized weights
lm.optimizedWeights.Performance -= learningRate * gradients.Performance
lm.optimizedWeights.Reliability -= learningRate * gradients.Reliability
lm.optimizedWeights.Cost -= learningRate * gradients.Cost
lm.optimizedWeights.Region -= learningRate * gradients.Region
lm.optimizedWeights.Capability -= learningRate * gradients.Capability
// Apply regularization to prevent overfitting
lm.applyRegularization()
// Ensure weights sum to 1 and are non-negative
lm.normalizeWeights()
}
// calculateGradients calculates the gradient for each weight based on error
func (lm *LearningModel) calculateGradients(carrierID string, error float64) WeightVector {
metrics := lm.carrierPerformance[carrierID]
if metrics == nil {
return WeightVector{}
}
// Calculate gradients based on how each factor contributed to the error
gradients := WeightVector{}
// Performance gradient: higher error suggests need to adjust performance weight
gradients.Performance = error * (metrics.Reliability - 0.5) * 2.0
// Reliability gradient: adjust based on success rate
gradients.Reliability = error * (metrics.SuccessRate - 0.5) * 2.0
// Cost gradient: adjust based on cost efficiency
gradients.Cost = error * (metrics.CostEfficiency - 0.5) * 2.0
// Region and capability gradients (simplified)
gradients.Region = error * 0.1
gradients.Capability = error * 0.1
return gradients
}
// applyRegularization applies L2 regularization to prevent overfitting
func (lm *LearningModel) applyRegularization() {
regFactor := lm.regularization
lm.optimizedWeights.Performance *= (1.0 - regFactor)
lm.optimizedWeights.Reliability *= (1.0 - regFactor)
lm.optimizedWeights.Cost *= (1.0 - regFactor)
lm.optimizedWeights.Region *= (1.0 - regFactor)
lm.optimizedWeights.Capability *= (1.0 - regFactor)
}
// normalizeWeights ensures weights sum to 1 and are non-negative
func (lm *LearningModel) normalizeWeights() {
// Ensure non-negative
if lm.optimizedWeights.Performance < 0 {
lm.optimizedWeights.Performance = 0
}
if lm.optimizedWeights.Reliability < 0 {
lm.optimizedWeights.Reliability = 0
}
if lm.optimizedWeights.Cost < 0 {
lm.optimizedWeights.Cost = 0
}
if lm.optimizedWeights.Region < 0 {
lm.optimizedWeights.Region = 0
}
if lm.optimizedWeights.Capability < 0 {
lm.optimizedWeights.Capability = 0
}
// Normalize to sum to 1
total := lm.optimizedWeights.Performance + lm.optimizedWeights.Reliability +
lm.optimizedWeights.Cost + lm.optimizedWeights.Region + lm.optimizedWeights.Capability
if total > 0 {
lm.optimizedWeights.Performance /= total
lm.optimizedWeights.Reliability /= total
lm.optimizedWeights.Cost /= total
lm.optimizedWeights.Region /= total
lm.optimizedWeights.Capability /= total
} else {
// Fallback to base weights if all weights become zero
lm.optimizedWeights = lm.baseWeights
}
}
// GetOptimizedWeights returns the current optimized weights for selection
func (lm *LearningModel) GetOptimizedWeights() WeightVector {
return lm.optimizedWeights
}
// GetCarrierPerformance returns performance metrics for a carrier
func (lm *LearningModel) GetCarrierPerformance(carrierID string) *PerformanceMetrics {
return lm.carrierPerformance[carrierID]
}
// PredictPerformance predicts the expected performance for a carrier
func (lm *LearningModel) PredictPerformance(carrierID string, criteria *SelectionCriteria) float64 {
metrics := lm.carrierPerformance[carrierID]
if metrics == nil {
return 75.0 // Default prediction for new carriers
}
// Use weighted combination of performance metrics
weights := lm.optimizedWeights
prediction := metrics.SuccessRate*weights.Reliability*100 +
(1.0-metrics.AverageResponseTime/1000.0)*weights.Performance*100 +
metrics.CostEfficiency*weights.Cost*100 +
0.5*weights.Region*100 + // Simplified region prediction
0.5*weights.Capability*100 // Simplified capability prediction
return math.Min(100.0, math.Max(0.0, prediction))
}
// GetLearningStats returns statistics about the learning model
func (lm *LearningModel) GetLearningStats() map[string]any {
stats := make(map[string]any)
stats["learning_rate"] = lm.learningRate
stats["total_carriers_tracked"] = len(lm.carrierPerformance)
stats["optimized_weights"] = lm.optimizedWeights
stats["base_weights"] = lm.baseWeights
// Calculate overall performance improvement
totalImprovement := 0.0
sampleCount := 0
for _, history := range lm.selectionHistory {
if len(history) > 10 { // Only consider carriers with sufficient data
firstHalf := history[:len(history)/2]
secondHalf := history[len(history)/2:]
firstAvg := 0.0
for _, record := range firstHalf {
firstAvg += record.ActualScore
}
firstAvg /= float64(len(firstHalf))
secondAvg := 0.0
for _, record := range secondHalf {
secondAvg += record.ActualScore
}
secondAvg /= float64(len(secondHalf))
improvement := secondAvg - firstAvg
totalImprovement += improvement
sampleCount++
}
}
if sampleCount > 0 {
stats["average_performance_improvement"] = totalImprovement / float64(sampleCount)
}
return stats
}