feat: 新增 thinking 注入配置支持,扩展设置管理与前端交互

新增 promptcompat 和 OpenAI shared 层的 thinking 注入逻辑,
完善配置系统的编解码与校验,更新设置管理 API 与前端 UI。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
CJACK
2026-04-26 13:35:20 +08:00
parent 3627c7366d
commit c09a4b51a5
34 changed files with 1038 additions and 94 deletions

View File

@@ -21,7 +21,7 @@ func boolFrom(v any) bool {
}
}
func parseSettingsUpdateRequest(req map[string]any) (*config.AdminConfig, *config.RuntimeConfig, *config.CompatConfig, *config.ResponsesConfig, *config.EmbeddingsConfig, *config.AutoDeleteConfig, *config.HistorySplitConfig, map[string]string, error) {
func parseSettingsUpdateRequest(req map[string]any) (*config.AdminConfig, *config.RuntimeConfig, *config.CompatConfig, *config.ResponsesConfig, *config.EmbeddingsConfig, *config.AutoDeleteConfig, *config.HistorySplitConfig, *config.CurrentInputFileConfig, *config.ThinkingInjectionConfig, map[string]string, error) {
var (
adminCfg *config.AdminConfig
runtimeCfg *config.RuntimeConfig
@@ -30,6 +30,8 @@ func parseSettingsUpdateRequest(req map[string]any) (*config.AdminConfig, *confi
embCfg *config.EmbeddingsConfig
autoDeleteCfg *config.AutoDeleteConfig
historySplitCfg *config.HistorySplitConfig
currentInputCfg *config.CurrentInputFileConfig
thinkingInjCfg *config.ThinkingInjectionConfig
aliasMap map[string]string
)
@@ -38,7 +40,7 @@ func parseSettingsUpdateRequest(req map[string]any) (*config.AdminConfig, *confi
if v, exists := raw["jwt_expire_hours"]; exists {
n := intFrom(v)
if err := config.ValidateIntRange("admin.jwt_expire_hours", n, 1, 720, true); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
cfg.JWTExpireHours = n
}
@@ -50,33 +52,33 @@ func parseSettingsUpdateRequest(req map[string]any) (*config.AdminConfig, *confi
if v, exists := raw["account_max_inflight"]; exists {
n := intFrom(v)
if err := config.ValidateIntRange("runtime.account_max_inflight", n, 1, 256, true); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
cfg.AccountMaxInflight = n
}
if v, exists := raw["account_max_queue"]; exists {
n := intFrom(v)
if err := config.ValidateIntRange("runtime.account_max_queue", n, 1, 200000, true); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
cfg.AccountMaxQueue = n
}
if v, exists := raw["global_max_inflight"]; exists {
n := intFrom(v)
if err := config.ValidateIntRange("runtime.global_max_inflight", n, 1, 200000, true); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
cfg.GlobalMaxInflight = n
}
if v, exists := raw["token_refresh_interval_hours"]; exists {
n := intFrom(v)
if err := config.ValidateIntRange("runtime.token_refresh_interval_hours", n, 1, 720, true); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
cfg.TokenRefreshIntervalHours = n
}
if cfg.AccountMaxInflight > 0 && cfg.GlobalMaxInflight > 0 && cfg.GlobalMaxInflight < cfg.AccountMaxInflight {
return nil, nil, nil, nil, nil, nil, nil, nil, fmt.Errorf("runtime.global_max_inflight must be >= runtime.account_max_inflight")
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, fmt.Errorf("runtime.global_max_inflight must be >= runtime.account_max_inflight")
}
runtimeCfg = cfg
}
@@ -99,7 +101,7 @@ func parseSettingsUpdateRequest(req map[string]any) (*config.AdminConfig, *confi
if v, exists := raw["store_ttl_seconds"]; exists {
n := intFrom(v)
if err := config.ValidateIntRange("responses.store_ttl_seconds", n, 30, 86400, true); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
cfg.StoreTTLSeconds = n
}
@@ -111,7 +113,7 @@ func parseSettingsUpdateRequest(req map[string]any) (*config.AdminConfig, *confi
if v, exists := raw["provider"]; exists {
p := strings.TrimSpace(fmt.Sprintf("%v", v))
if err := config.ValidateTrimmedString("embeddings.provider", p, false); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
cfg.Provider = p
}
@@ -137,7 +139,7 @@ func parseSettingsUpdateRequest(req map[string]any) (*config.AdminConfig, *confi
if v, exists := raw["mode"]; exists {
mode := strings.ToLower(strings.TrimSpace(fmt.Sprintf("%v", v)))
if err := config.ValidateAutoDeleteMode(mode); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
if mode == "" {
mode = "none"
@@ -152,20 +154,71 @@ func parseSettingsUpdateRequest(req map[string]any) (*config.AdminConfig, *confi
if raw, ok := req["history_split"].(map[string]any); ok {
cfg := &config.HistorySplitConfig{}
enabled := true
cfg.Enabled = &enabled
if v, exists := raw["enabled"]; exists {
enabled := boolFrom(v)
cfg.Enabled = &enabled
}
if v, exists := raw["trigger_after_turns"]; exists {
n := intFrom(v)
if err := config.ValidateIntRange("history_split.trigger_after_turns", n, 1, 1000, true); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
cfg.TriggerAfterTurns = &n
}
if err := config.ValidateHistorySplitConfig(*cfg); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, err
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
historySplitCfg = cfg
}
return adminCfg, runtimeCfg, compatCfg, respCfg, embCfg, autoDeleteCfg, historySplitCfg, aliasMap, nil
if raw, ok := req["current_input_file"].(map[string]any); ok {
cfg := &config.CurrentInputFileConfig{}
if v, exists := raw["enabled"]; exists {
enabled := boolFrom(v)
cfg.Enabled = &enabled
}
if v, exists := raw["min_chars"]; exists {
n := intFrom(v)
if err := config.ValidateIntRange("current_input_file.min_chars", n, 0, 100000000, true); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
cfg.MinChars = n
}
if err := config.ValidateCurrentInputFileConfig(*cfg); err != nil {
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err
}
currentInputCfg = cfg
}
if boolPtrValue(historySplitCfgEnabled(historySplitCfg)) && boolPtrValue(currentInputCfgEnabled(currentInputCfg)) {
return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, fmt.Errorf("history_split.enabled and current_input_file.enabled cannot both be true")
}
if raw, ok := req["thinking_injection"].(map[string]any); ok {
cfg := &config.ThinkingInjectionConfig{}
if v, exists := raw["enabled"]; exists {
b := boolFrom(v)
cfg.Enabled = &b
}
thinkingInjCfg = cfg
}
return adminCfg, runtimeCfg, compatCfg, respCfg, embCfg, autoDeleteCfg, historySplitCfg, currentInputCfg, thinkingInjCfg, aliasMap, nil
}
func historySplitCfgEnabled(cfg *config.HistorySplitConfig) *bool {
if cfg == nil {
return nil
}
return cfg.Enabled
}
func currentInputCfgEnabled(cfg *config.CurrentInputFileConfig) *bool {
if cfg == nil {
return nil
}
return cfg.Enabled
}
func boolPtrValue(v *bool) bool {
return v != nil && *v
}