| @@ -263,7 +263,6 @@ func (m *Manager) Execute(ctx context.Context, providers []string, req cliproxye · 262 unmodified lines |
| 263 | return cliproxyexecutor.Response{}, &Error{Code: "provider_not_found", Message: "no provider supplied"} | 263 | return cliproxyexecutor.Response{}, &Error{Code: "provider_not_found", Message: "no provider supplied"} |
| 264 | } | 264 | } |
| 265 | rotated := m.rotateProviders(req.Model, normalized) | 265 | rotated := m.rotateProviders(req.Model, normalized) |
| 266 | defer m.advanceProviderCursor(req.Model, normalized) | | |
| 267 | | 266 | |
| 268 | retryTimes, maxWait := m.retrySettings() | 267 | retryTimes, maxWait := m.retrySettings() |
| 269 | attempts := retryTimes + 1 | 268 | attempts := retryTimes + 1 |
| @@ -302,7 +301,6 @@ func (m *Manager) ExecuteCount(ctx context.Context, providers []string, req clip · 32 unmodified lines |
| 302 | return cliproxyexecutor.Response{}, &Error{Code: "provider_not_found", Message: "no provider supplied"} | 301 | return cliproxyexecutor.Response{}, &Error{Code: "provider_not_found", Message: "no provider supplied"} |
| 303 | } | 302 | } |
| 304 | rotated := m.rotateProviders(req.Model, normalized) | 303 | rotated := m.rotateProviders(req.Model, normalized) |
| 305 | defer m.advanceProviderCursor(req.Model, normalized) | | |
| 306 | | 304 | |
| 307 | retryTimes, maxWait := m.retrySettings() | 305 | retryTimes, maxWait := m.retrySettings() |
| 308 | attempts := retryTimes + 1 | 306 | attempts := retryTimes + 1 |
| @@ -341,7 +339,6 @@ func (m *Manager) ExecuteStream(ctx context.Context, providers []string, req cli · 32 unmodified lines |
| 341 | return nil, &Error{Code: "provider_not_found", Message: "no provider supplied"} | 339 | return nil, &Error{Code: "provider_not_found", Message: "no provider supplied"} |
| 342 | } | 340 | } |
| 343 | rotated := m.rotateProviders(req.Model, normalized) | 341 | rotated := m.rotateProviders(req.Model, normalized) |
| 344 | defer m.advanceProviderCursor(req.Model, normalized) | | |
| 345 | | 342 | |
| 346 | retryTimes, maxWait := m.retrySettings() | 343 | retryTimes, maxWait := m.retrySettings() |
| 347 | attempts := retryTimes + 1 | 344 | attempts := retryTimes + 1 |
| @@ -640,13 +637,20 @@ func (m *Manager) normalizeProviders(providers []string) []string { · 292 unmodified lines |
| 640 | return result | 637 | return result |
| 641 | } | 638 | } |
| 642 | | 639 | |
| | 640 | // rotateProviders returns a rotated view of the providers list starting from the |
| | 641 | // current offset for the model, and atomically increments the offset for the next call. |
| | 642 | // This ensures concurrent requests get different starting providers. |
| 643 | func (m *Manager) rotateProviders(model string, providers []string) []string { | 643 | func (m *Manager) rotateProviders(model string, providers []string) []string { |
| 644 | if len(providers) == 0 { | 644 | if len(providers) == 0 { |
| 645 | return nil | 645 | return nil |
| 646 | } | 646 | } |
| 647 | m.mu.RLock() | 647 | |
| | 648 | // Atomic read-and-increment: get current offset and advance cursor in one lock |
| | 649 | m.mu.Lock() |
| 648 | offset := m.providerOffsets[model] | 650 | offset := m.providerOffsets[model] |
| 649 | m.mu.RUnlock() | 651 | m.providerOffsets[model] = (offset + 1) % len(providers) |
| | 652 | m.mu.Unlock() |
| | 653 | |
| 650 | if len(providers) > 0 { | 654 | if len(providers) > 0 { |
| 651 | offset %= len(providers) | 655 | offset %= len(providers) |
| 652 | } | 656 | } |
| @@ -662,19 +666,6 @@ func (m *Manager) rotateProviders(model string, providers []string) []string { · 9 unmodified lines |
| 662 | return rotated | 666 | return rotated |
| 663 | } | 667 | } |
| 664 | | 668 | |
| 665 | func (m *Manager) advanceProviderCursor(model string, providers []string) { | | |
| 666 | if len(providers) == 0 { | | |
| 667 | m.mu.Lock() | | |
| 668 | delete(m.providerOffsets, model) | | |
| 669 | m.mu.Unlock() | | |
| 670 | return | | |
| 671 | } | | |
| 672 | m.mu.Lock() | | |
| 673 | current := m.providerOffsets[model] | | |
| 674 | m.providerOffsets[model] = (current + 1) % len(providers) | | |
| 675 | m.mu.Unlock() | | |
| 676 | } | | |
| 677 | | | |
| 678 | func (m *Manager) retrySettings() (int, time.Duration) { | 669 | func (m *Manager) retrySettings() (int, time.Duration) { |
| 679 | if m == nil { | 670 | if m == nil { |
| 680 | return 0, 0 | 671 | return 0, 0 |