package providers import ( "context" "time" "github.com/sony/gobreaker" "gophergate/internal/models" ) type CircuitBreakerProvider struct { provider Provider cb *gobreaker.CircuitBreaker } func NewCircuitBreakerProvider(p Provider) Provider { name := p.Name() var maxRequests uint32 = 5 var interval = 60 * time.Second var timeout = 5 * time.Minute settings := gobreaker.Settings{ Name: name, MaxRequests: maxRequests, Interval: interval, Timeout: timeout, ReadyToTrip: func(counts gobreaker.Counts) bool { // Trip after 3 consecutive failures return counts.ConsecutiveFailures > 3 }, } return &CircuitBreakerProvider{ provider: p, cb: gobreaker.NewCircuitBreaker(settings), } } func (cbp *CircuitBreakerProvider) Name() string { return cbp.provider.Name() } func (cbp *CircuitBreakerProvider) ChatCompletion(ctx context.Context, req *models.UnifiedRequest) (*models.ChatCompletionResponse, error) { result, err := cbp.cb.Execute(func() (interface{}, error) { return cbp.provider.ChatCompletion(ctx, req) }) if err != nil { return nil, err } return result.(*models.ChatCompletionResponse), nil } func (cbp *CircuitBreakerProvider) ChatCompletionStream(ctx context.Context, req *models.UnifiedRequest) (<-chan *models.ChatCompletionStreamResponse, error) { // Circuit breaker for streaming is tricky. We'll just call the provider directly. // Future: Implement a way to track stream failures in the circuit breaker. return cbp.provider.ChatCompletionStream(ctx, req) } func (cbp *CircuitBreakerProvider) ImageGeneration(ctx context.Context, req *models.ImageGenerationRequest) (*models.ImageGenerationResponse, error) { result, err := cbp.cb.Execute(func() (interface{}, error) { return cbp.provider.ImageGeneration(ctx, req) }) if err != nil { return nil, err } return result.(*models.ImageGenerationResponse), nil } func (cbp *CircuitBreakerProvider) Responses(ctx context.Context, req *models.ResponsesRequest) (*models.ResponsesResponse, error) { result, err := cbp.cb.Execute(func() (interface{}, error) { return cbp.provider.Responses(ctx, req) }) if err != nil { return nil, err } return result.(*models.ResponsesResponse), nil } func (cbp *CircuitBreakerProvider) ResponsesStream(ctx context.Context, req *models.ResponsesRequest) (<-chan *models.ResponsesStreamChunk, error) { // Circuit breaker passthrough for streaming (same pattern as ChatCompletionStream) return cbp.provider.ResponsesStream(ctx, req) }