mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
84 lines
3.2 KiB
Go
84 lines
3.2 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
)
|
|
|
|
// ModelAvailabilityDiagnosis describes whether the requested model can be
|
|
// served by any configured account in the group, ignoring transient state
|
|
// (rate limits, quota auto-pause, runtime blocks). Handlers use this on the
|
|
// "no available accounts" error path to distinguish 404 model_not_found from
|
|
// 503 service_unavailable.
|
|
type ModelAvailabilityDiagnosis struct {
|
|
// HasAccountsInPool is true if the group has at least one schedulable
|
|
// account on the queried platform (or, for Anthropic/Gemini, on the
|
|
// platform plus mixed-scheduled Antigravity accounts).
|
|
HasAccountsInPool bool
|
|
// HasModelSupport is true if at least one account's model mapping admits
|
|
// the requested model.
|
|
HasModelSupport bool
|
|
}
|
|
|
|
// ModelAvailabilityDiagnoser is implemented by gateway services that can
|
|
// report whether the requested model is configured to be served by any
|
|
// account. Both *GatewayService and *OpenAIGatewayService implement this so
|
|
// handlers in either package can share a single classifier.
|
|
type ModelAvailabilityDiagnoser interface {
|
|
DiagnoseModelAvailabilityForPlatform(
|
|
ctx context.Context,
|
|
groupID *int64,
|
|
requestedModel string,
|
|
platform string,
|
|
) ModelAvailabilityDiagnosis
|
|
}
|
|
|
|
// DiagnoseModelAvailabilityForPlatform inspects schedulable accounts of the
|
|
// given platform and returns whether the requested model is configured to be
|
|
// served by any of them. It deliberately ignores schedulability, rate limits,
|
|
// quotas, and runtime blocks — those are transient.
|
|
//
|
|
// Safe to call on the error path: returns {true,true} on any internal failure
|
|
// or when the inputs preclude meaningful diagnosis (empty model, etc.), so
|
|
// callers stay on the 503 fallback branch.
|
|
func (s *GatewayService) DiagnoseModelAvailabilityForPlatform(
|
|
ctx context.Context,
|
|
groupID *int64,
|
|
requestedModel string,
|
|
platform string,
|
|
) ModelAvailabilityDiagnosis {
|
|
if s == nil {
|
|
return ModelAvailabilityDiagnosis{HasAccountsInPool: true, HasModelSupport: true}
|
|
}
|
|
requestedModel = strings.TrimSpace(requestedModel)
|
|
if requestedModel == "" {
|
|
// No model specified — cannot decide model_not_found. Caller falls back to 503.
|
|
return ModelAvailabilityDiagnosis{HasAccountsInPool: true, HasModelSupport: true}
|
|
}
|
|
if strings.TrimSpace(platform) == "" {
|
|
// Without a platform we cannot scope the lookup; bail out to the
|
|
// 503 branch rather than make an unscoped scan.
|
|
return ModelAvailabilityDiagnosis{HasAccountsInPool: true, HasModelSupport: true}
|
|
}
|
|
|
|
// hasForcePlatform=false so Anthropic/Gemini also surface mixed-scheduled
|
|
// Antigravity accounts, matching what selection would consider.
|
|
accounts, _, err := s.listSchedulableAccounts(ctx, groupID, platform, false)
|
|
if err != nil {
|
|
// Conservative fallback: pretend everything is fine so the caller
|
|
// returns 503 (we don't want to flip to 404 just because a lookup
|
|
// hiccup'd).
|
|
return ModelAvailabilityDiagnosis{HasAccountsInPool: true, HasModelSupport: true}
|
|
}
|
|
|
|
diag := ModelAvailabilityDiagnosis{}
|
|
for i := range accounts {
|
|
diag.HasAccountsInPool = true
|
|
if s.isModelSupportedByAccountWithContext(ctx, &accounts[i], requestedModel) {
|
|
diag.HasModelSupport = true
|
|
return diag
|
|
}
|
|
}
|
|
return diag
|
|
}
|