mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
Skip OpenAI messages-dispatch gpt-5.x defaults for OpenCode groups and composite OpenCode targets. Filter unpriced claude-* billing candidates, estimate count_tokens locally, sanitize body session IDs before setting X-OpenCode-Session, and treat Zen 402 as recoverable payg cooldown. Stop rewriting the inbound body before protocol routing so account model_mapping applies once and usage_logs.model stays the client name.
125 lines
3.7 KiB
Go
125 lines
3.7 KiB
Go
package service
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/pkg/xai"
|
|
)
|
|
|
|
const (
|
|
defaultOpenAIMessagesDispatchOpusMappedModel = "gpt-5.4"
|
|
defaultOpenAIMessagesDispatchSonnetMappedModel = "gpt-5.3-codex"
|
|
defaultOpenAIMessagesDispatchHaikuMappedModel = "gpt-5.4-mini"
|
|
)
|
|
|
|
func normalizeOpenAIMessagesDispatchMappedModel(model string) string {
|
|
model = NormalizeOpenAICompatRequestedModel(strings.TrimSpace(model))
|
|
return strings.TrimSpace(model)
|
|
}
|
|
|
|
func normalizeOpenAIMessagesDispatchModelConfig(cfg OpenAIMessagesDispatchModelConfig) OpenAIMessagesDispatchModelConfig {
|
|
out := OpenAIMessagesDispatchModelConfig{
|
|
OpusMappedModel: normalizeOpenAIMessagesDispatchMappedModel(cfg.OpusMappedModel),
|
|
SonnetMappedModel: normalizeOpenAIMessagesDispatchMappedModel(cfg.SonnetMappedModel),
|
|
HaikuMappedModel: normalizeOpenAIMessagesDispatchMappedModel(cfg.HaikuMappedModel),
|
|
}
|
|
|
|
if len(cfg.ExactModelMappings) > 0 {
|
|
out.ExactModelMappings = make(map[string]string, len(cfg.ExactModelMappings))
|
|
for requestedModel, mappedModel := range cfg.ExactModelMappings {
|
|
requestedModel = strings.TrimSpace(requestedModel)
|
|
mappedModel = normalizeOpenAIMessagesDispatchMappedModel(mappedModel)
|
|
if requestedModel == "" || mappedModel == "" {
|
|
continue
|
|
}
|
|
out.ExactModelMappings[requestedModel] = mappedModel
|
|
}
|
|
if len(out.ExactModelMappings) == 0 {
|
|
out.ExactModelMappings = nil
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func claudeMessagesDispatchFamily(model string) string {
|
|
normalized := strings.ToLower(strings.TrimSpace(model))
|
|
if !strings.HasPrefix(normalized, "claude") {
|
|
return ""
|
|
}
|
|
switch {
|
|
case strings.Contains(normalized, "opus"):
|
|
return "opus"
|
|
case strings.Contains(normalized, "sonnet"):
|
|
return "sonnet"
|
|
case strings.Contains(normalized, "haiku"):
|
|
return "haiku"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func (g *Group) ResolveMessagesDispatchModel(requestedModel string) string {
|
|
if g == nil {
|
|
return ""
|
|
}
|
|
requestedModel = strings.TrimSpace(requestedModel)
|
|
if requestedModel == "" {
|
|
return ""
|
|
}
|
|
|
|
if g.Platform == PlatformGrok {
|
|
if claudeMessagesDispatchFamily(requestedModel) == "" {
|
|
return ""
|
|
}
|
|
opts := xai.RuntimeModelMappingOptions()
|
|
if !opts.EnableCrossClientMap {
|
|
return ""
|
|
}
|
|
return xai.ModelMappingWithOptions(opts)["claude-*"]
|
|
}
|
|
|
|
// 国产供应商 / OpenCode 分组:调度级模型映射不适用(其配置被 sanitize 置空,
|
|
// 且下方的 gpt-5.x 默认值是 openai 专属,发给这些上游必错)。模型改写完全
|
|
// 交给账号级 model_mapping;Anthropic 协议上游本身接受 claude-* 模型名。
|
|
if IsMultiProtocolAPIKeyProvider(g.Platform) {
|
|
return ""
|
|
}
|
|
|
|
cfg := normalizeOpenAIMessagesDispatchModelConfig(g.MessagesDispatchModelConfig)
|
|
if mappedModel := strings.TrimSpace(cfg.ExactModelMappings[requestedModel]); mappedModel != "" {
|
|
return mappedModel
|
|
}
|
|
|
|
switch claudeMessagesDispatchFamily(requestedModel) {
|
|
case "opus":
|
|
if mappedModel := strings.TrimSpace(cfg.OpusMappedModel); mappedModel != "" {
|
|
return mappedModel
|
|
}
|
|
return defaultOpenAIMessagesDispatchOpusMappedModel
|
|
case "sonnet":
|
|
if mappedModel := strings.TrimSpace(cfg.SonnetMappedModel); mappedModel != "" {
|
|
return mappedModel
|
|
}
|
|
return defaultOpenAIMessagesDispatchSonnetMappedModel
|
|
case "haiku":
|
|
if mappedModel := strings.TrimSpace(cfg.HaikuMappedModel); mappedModel != "" {
|
|
return mappedModel
|
|
}
|
|
return defaultOpenAIMessagesDispatchHaikuMappedModel
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func sanitizeGroupMessagesDispatchFields(g *Group) {
|
|
if g == nil || g.Platform == PlatformOpenAI {
|
|
return
|
|
}
|
|
if g.Platform != PlatformComposite {
|
|
g.AllowMessagesDispatch = false
|
|
}
|
|
g.DefaultMappedModel = ""
|
|
g.MessagesDispatchModelConfig = OpenAIMessagesDispatchModelConfig{}
|
|
}
|