feat(openai): port /responses/compact account support flow (PR #1555)
将 vansour/sub2api#1555 的 OpenAI compact 能力建模手工移植到当前 main:账号 级 compact 状态/auto-force_on-force_off 模式、compact-only 模型映射、调度器 tier 分层(已支持 > 未知 > 已知不支持)、管理后台 compact 主动探测,以及对应 i18n/状态徽章。普通 /responses 流量行为不变,无数据库迁移。
This commit is contained in:
@@ -393,6 +393,56 @@ func parseTempUnschedInt(value any) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
const (
|
||||
// OpenAICompactModeAuto follows compact-probe results when deciding compact eligibility.
|
||||
OpenAICompactModeAuto = "auto"
|
||||
// OpenAICompactModeForceOn always treats the account as compact-supported.
|
||||
OpenAICompactModeForceOn = "force_on"
|
||||
// OpenAICompactModeForceOff always treats the account as compact-unsupported.
|
||||
OpenAICompactModeForceOff = "force_off"
|
||||
)
|
||||
|
||||
func normalizeOpenAICompactMode(mode string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(mode)) {
|
||||
case OpenAICompactModeForceOn:
|
||||
return OpenAICompactModeForceOn
|
||||
case OpenAICompactModeForceOff:
|
||||
return OpenAICompactModeForceOff
|
||||
default:
|
||||
return OpenAICompactModeAuto
|
||||
}
|
||||
}
|
||||
|
||||
func stringMappingFromRaw(raw any) map[string]string {
|
||||
switch mapping := raw.(type) {
|
||||
case map[string]any:
|
||||
if len(mapping) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make(map[string]string, len(mapping))
|
||||
for key, value := range mapping {
|
||||
if str, ok := value.(string); ok {
|
||||
result[key] = str
|
||||
}
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
case map[string]string:
|
||||
if len(mapping) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make(map[string]string, len(mapping))
|
||||
for key, value := range mapping {
|
||||
result[key] = value
|
||||
}
|
||||
return result
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Account) GetModelMapping() map[string]string {
|
||||
credentialsPtr := mapPtr(a.Credentials)
|
||||
rawMapping, _ := a.Credentials["model_mapping"].(map[string]any)
|
||||
@@ -598,6 +648,77 @@ func (a *Account) ResolveMappedModel(requestedModel string) (mappedModel string,
|
||||
return requestedModel, false
|
||||
}
|
||||
|
||||
// GetOpenAICompactMode returns the compact routing mode for an OpenAI account.
|
||||
// Missing or invalid values fall back to "auto".
|
||||
func (a *Account) GetOpenAICompactMode() string {
|
||||
if a == nil || !a.IsOpenAI() || a.Extra == nil {
|
||||
return OpenAICompactModeAuto
|
||||
}
|
||||
mode, _ := a.Extra["openai_compact_mode"].(string)
|
||||
return normalizeOpenAICompactMode(mode)
|
||||
}
|
||||
|
||||
// OpenAICompactSupportKnown reports whether compact capability is known for this
|
||||
// account and, when known, whether it is supported.
|
||||
func (a *Account) OpenAICompactSupportKnown() (supported bool, known bool) {
|
||||
if a == nil || !a.IsOpenAI() {
|
||||
return false, false
|
||||
}
|
||||
|
||||
switch a.GetOpenAICompactMode() {
|
||||
case OpenAICompactModeForceOn:
|
||||
return true, true
|
||||
case OpenAICompactModeForceOff:
|
||||
return false, true
|
||||
}
|
||||
|
||||
if a.Extra == nil {
|
||||
return false, false
|
||||
}
|
||||
supported, ok := a.Extra["openai_compact_supported"].(bool)
|
||||
if !ok {
|
||||
return false, false
|
||||
}
|
||||
return supported, true
|
||||
}
|
||||
|
||||
// AllowsOpenAICompact reports whether the account may be considered for compact
|
||||
// requests. Unknown capability remains allowed to avoid breaking older accounts
|
||||
// before an explicit probe has been run.
|
||||
func (a *Account) AllowsOpenAICompact() bool {
|
||||
if a == nil || !a.IsOpenAI() {
|
||||
return false
|
||||
}
|
||||
supported, known := a.OpenAICompactSupportKnown()
|
||||
if !known {
|
||||
return true
|
||||
}
|
||||
return supported
|
||||
}
|
||||
|
||||
// GetCompactModelMapping returns compact-only model remapping configuration.
|
||||
// This mapping is intended for /responses/compact only and does not affect
|
||||
// normal /responses traffic.
|
||||
func (a *Account) GetCompactModelMapping() map[string]string {
|
||||
if a == nil || a.Credentials == nil {
|
||||
return nil
|
||||
}
|
||||
return stringMappingFromRaw(a.Credentials["compact_model_mapping"])
|
||||
}
|
||||
|
||||
// ResolveCompactMappedModel resolves compact-only model remapping and reports
|
||||
// whether a compact-specific mapping rule matched.
|
||||
func (a *Account) ResolveCompactMappedModel(requestedModel string) (mappedModel string, matched bool) {
|
||||
mapping := a.GetCompactModelMapping()
|
||||
if len(mapping) == 0 {
|
||||
return requestedModel, false
|
||||
}
|
||||
if mappedModel, matched := resolveRequestedModelInMapping(mapping, requestedModel); matched {
|
||||
return mappedModel, true
|
||||
}
|
||||
return requestedModel, false
|
||||
}
|
||||
|
||||
func (a *Account) GetBaseURL() string {
|
||||
if a.Type != AccountTypeAPIKey {
|
||||
return ""
|
||||
|
||||
Reference in New Issue
Block a user