mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-07 16:48:45 +08:00
后端: - 协议凭证维度 credentials[api_protocol] ∈ chat_completions(默认)/anthropic/responses(deepseek) - /v1/messages 零转换直通原生 Anthropic 端点(kimi/zhipu/deepseek),CC/Responses 入站交叉组合走 apicompat 双向转换链(responses/chat_completions anthropic-native 转发器) - count_tokens:anthropic 协议透传原生端点;其余 CN 协议本地 tiktoken 估算 - Coding Plan 额度探测(5h/weekly 滚动窗口)+ payg 余额探测(kimi/deepseek), deepseek 双币种 CNY+USD 明细,任一币种达标不停调 - 周期任务 [CNBalance] 并发探测 + 预算随工作量放大;响应式 429 冷却到最早窗口 重置点;余额不足可恢复临时停调;智谱 CREDIT_LIMIT 不污染窗口解析 - CC→anthropic 流式客户端断开后继续排水上游保住 usage 计量 前端: - 创建/编辑弹窗 account_mode + api_protocol + base_url 联动预设(含 watcher 竞态防护) - 用量单元格:kimi/zhipu coding 显示 5h/weekly 窗口,kimi/deepseek payg 显示余额, 多币种并列展示;探测失败保留快照;挂载自动探测 5min 去抖 - 调度阈值设置面板补 kimi/zhipu 平台(对齐后端 AllowedSchedulingThresholdPlatforms)
70 lines
2.3 KiB
Go
70 lines
2.3 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/config"
|
|
)
|
|
|
|
// DiagnoseModelAvailabilityForPlatform reports whether the requested model
|
|
// is configured to be served by any persistently eligible OpenAI-compatible
|
|
// account in the group for the given platform (e.g. PlatformOpenAI,
|
|
// PlatformGrok). The platform scopes the candidate pool so distinct
|
|
// OpenAI-compatible platforms do not cross-contaminate diagnosis results.
|
|
// The query bypasses scheduler snapshots and ignores transient runtime state.
|
|
//
|
|
// Safe to call on the error path: returns {true,true} on any internal
|
|
// failure or when the inputs preclude meaningful diagnosis (empty model,
|
|
// nil service), so callers stay on the 503 fallback branch.
|
|
func (s *OpenAIGatewayService) 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 == "" {
|
|
return ModelAvailabilityDiagnosis{HasAccountsInPool: true, HasModelSupport: true}
|
|
}
|
|
if s.accountRepo == nil {
|
|
return ModelAvailabilityDiagnosis{HasAccountsInPool: true, HasModelSupport: true}
|
|
}
|
|
|
|
platform = NormalizeOpenAICompatiblePlatform(platform)
|
|
queryGroupID := groupID
|
|
includeGrouped := false
|
|
if s.cfg != nil && s.cfg.RunMode == config.RunModeSimple {
|
|
queryGroupID = nil
|
|
includeGrouped = true
|
|
}
|
|
accounts, err := s.accountRepo.ListModelAvailabilityCandidates(
|
|
ctx,
|
|
queryGroupID,
|
|
[]string{platform},
|
|
includeGrouped,
|
|
)
|
|
if err != nil {
|
|
// Conservative fallback so the caller keeps returning 503; we do not
|
|
// want a transient lookup failure to flip into 404 model_not_found.
|
|
return ModelAvailabilityDiagnosis{HasAccountsInPool: true, HasModelSupport: true}
|
|
}
|
|
|
|
diag := ModelAvailabilityDiagnosis{}
|
|
for i := range accounts {
|
|
diag.HasAccountsInPool = true
|
|
// Mirrors the per-candidate filter used during account selection
|
|
// (openai_account_scheduler.isAccountRequestCompatible): empty
|
|
// model_mapping accepts everything; otherwise the explicit / wildcard
|
|
// mapping must match.
|
|
if accounts[i].IsModelSupported(requestedModel) {
|
|
diag.HasModelSupport = true
|
|
return diag
|
|
}
|
|
}
|
|
return diag
|
|
}
|