Files
GopherGate/internal/providers/circuit_breaker.go
T
hobokenchicken 5ee539d95c
CI / Lint (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled
feat: add image generation for OpenAI DALL-E and Gemini Imagen
New `/v1/images/generations` endpoint proxies DALL-E 2/3 (OpenAI)
and Imagen 3 (Gemini). Same auth/logging as chat completions.

- Add ImageGenerationRequest/Response models
- Extend Provider interface with ImageGeneration()
- OpenAI: forward to /v1/images/generations
- Gemini: call /v1beta/models/{model}:predict, map OpenAI params
- Circuit breaker wraps image gen like chat completions
- Model routing: dall-e* -> openai, imagen*/gemini* -> gemini
- Unsupported providers (deepseek/moonshot/grok/ollama) return error
- Fix pre-existing CachedContentTokenCount bug in StreamGemini
2026-04-27 10:06:07 -04:00

67 lines
1.9 KiB
Go

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 = 30 * time.Second
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
}