mirror of
https://github.com/aljazceru/ollama-openrouter-proxy.git
synced 2025-12-17 23:34:21 +01:00
148 lines
3.9 KiB
Go
148 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"log/slog"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// PermanentFailureTracker tracks models that should be skipped for the entire runtime
|
|
type PermanentFailureTracker struct {
|
|
mu sync.RWMutex
|
|
permanentFailed map[string]time.Time // Models that are permanently unavailable (404, etc)
|
|
temporaryFailed map[string]time.Time // Models that are temporarily unavailable
|
|
}
|
|
|
|
// NewPermanentFailureTracker creates a new tracker
|
|
func NewPermanentFailureTracker() *PermanentFailureTracker {
|
|
return &PermanentFailureTracker{
|
|
permanentFailed: make(map[string]time.Time),
|
|
temporaryFailed: make(map[string]time.Time),
|
|
}
|
|
}
|
|
|
|
// MarkPermanentFailure marks a model as permanently failed (404, model not found)
|
|
func (p *PermanentFailureTracker) MarkPermanentFailure(model string) {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
p.permanentFailed[model] = time.Now()
|
|
slog.Warn("Model marked as permanently unavailable for this session", "model", model)
|
|
}
|
|
|
|
// MarkTemporaryFailure marks a model as temporarily failed
|
|
func (p *PermanentFailureTracker) MarkTemporaryFailure(model string) {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
p.temporaryFailed[model] = time.Now()
|
|
}
|
|
|
|
// IsPermanentlyFailed checks if a model is permanently failed
|
|
func (p *PermanentFailureTracker) IsPermanentlyFailed(model string) bool {
|
|
p.mu.RLock()
|
|
defer p.mu.RUnlock()
|
|
_, exists := p.permanentFailed[model]
|
|
return exists
|
|
}
|
|
|
|
// ShouldSkip checks if a model should be skipped (either permanent or recent temporary failure)
|
|
func (p *PermanentFailureTracker) ShouldSkip(model string) bool {
|
|
p.mu.RLock()
|
|
defer p.mu.RUnlock()
|
|
|
|
// Check permanent failures
|
|
if _, exists := p.permanentFailed[model]; exists {
|
|
return true
|
|
}
|
|
|
|
// Check temporary failures (5 minute cooldown)
|
|
if failTime, exists := p.temporaryFailed[model]; exists {
|
|
if time.Since(failTime) < 5*time.Minute {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// ClearTemporaryFailure removes a model from temporary failures on success
|
|
func (p *PermanentFailureTracker) ClearTemporaryFailure(model string) {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
delete(p.temporaryFailed, model)
|
|
}
|
|
|
|
// GetStats returns statistics about failures
|
|
func (p *PermanentFailureTracker) GetStats() (permanent int, temporary int) {
|
|
p.mu.RLock()
|
|
defer p.mu.RUnlock()
|
|
|
|
permanent = len(p.permanentFailed)
|
|
|
|
// Count non-expired temporary failures
|
|
now := time.Now()
|
|
for _, failTime := range p.temporaryFailed {
|
|
if now.Sub(failTime) < 5*time.Minute {
|
|
temporary++
|
|
}
|
|
}
|
|
|
|
return permanent, temporary
|
|
}
|
|
|
|
// isPermanentError determines if an error indicates a permanent failure
|
|
func isPermanentError(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
errStr := strings.ToLower(err.Error())
|
|
|
|
// 404 errors mean the model doesn't exist
|
|
if strings.Contains(errStr, "404") || strings.Contains(errStr, "not found") {
|
|
return true
|
|
}
|
|
|
|
// Model endpoint errors
|
|
if strings.Contains(errStr, "no endpoints found") {
|
|
return true
|
|
}
|
|
|
|
// Model not available errors
|
|
if strings.Contains(errStr, "model not available") || strings.Contains(errStr, "model does not exist") {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// isTemporaryError determines if an error is temporary (rate limits, timeouts, etc)
|
|
func isTemporaryError(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
errStr := strings.ToLower(err.Error())
|
|
|
|
// Rate limit errors
|
|
if strings.Contains(errStr, "429") || strings.Contains(errStr, "rate limit") || strings.Contains(errStr, "too many requests") {
|
|
return true
|
|
}
|
|
|
|
// Timeout errors
|
|
if strings.Contains(errStr, "timeout") || strings.Contains(errStr, "deadline exceeded") {
|
|
return true
|
|
}
|
|
|
|
// Temporary server errors
|
|
if strings.Contains(errStr, "503") || strings.Contains(errStr, "service unavailable") {
|
|
return true
|
|
}
|
|
|
|
// Connection errors
|
|
if strings.Contains(errStr, "connection refused") || strings.Contains(errStr, "connection reset") {
|
|
return true
|
|
}
|
|
|
|
return true // Default to temporary for unknown errors
|
|
} |