package app import ( "fmt" "regexp" "sort" "strings" "git.echol.cn/loser/ai_proxy/server/model/app" "git.echol.cn/loser/ai_proxy/server/model/app/request" ) // PresetInjector 预设注入器 type PresetInjector struct { preset *app.AiPreset } // NewPresetInjector 创建预设注入器 func NewPresetInjector(preset *app.AiPreset) *PresetInjector { return &PresetInjector{preset: preset} } // InjectMessages 注入预设到消息列表 func (p *PresetInjector) InjectMessages(messages []request.ChatMessage) []request.ChatMessage { if p.preset == nil || len(p.preset.Prompts) == 0 { return messages } // 1. 应用用户输入前的正则替换 messages = p.applyRegexScripts(messages, 1) // 2. 获取启用的提示词并排序 enabledPrompts := p.getEnabledPrompts() // 3. 构建注入后的消息列表 injectedMessages := p.buildInjectedMessages(messages, enabledPrompts) return injectedMessages } // getEnabledPrompts 获取启用的提示词并按注入顺序排序 func (p *PresetInjector) getEnabledPrompts() []app.PresetPrompt { var prompts []app.PresetPrompt for _, prompt := range p.preset.Prompts { if prompt.Enabled && !prompt.Marker { prompts = append(prompts, prompt) } } // 按 injection_order 和 injection_depth 排序 sort.Slice(prompts, func(i, j int) bool { if prompts[i].InjectionOrder != prompts[j].InjectionOrder { return prompts[i].InjectionOrder < prompts[j].InjectionOrder } return prompts[i].InjectionDepth < prompts[j].InjectionDepth }) return prompts } // buildInjectedMessages 构建注入后的消息列表 func (p *PresetInjector) buildInjectedMessages(messages []request.ChatMessage, prompts []app.PresetPrompt) []request.ChatMessage { result := make([]request.ChatMessage, 0) // 分离系统提示词和对话消息 var systemPrompts []app.PresetPrompt var otherPrompts []app.PresetPrompt for _, prompt := range prompts { if prompt.Role == "system" { systemPrompts = append(systemPrompts, prompt) } else { otherPrompts = append(otherPrompts, prompt) } } // 1. 先添加系统提示词 for _, prompt := range systemPrompts { result = append(result, request.ChatMessage{ Role: "system", Content: p.processPromptContent(prompt.Content), }) } // 2. 处理对话历史注入 chatHistoryIndex := p.findMarkerIndex("chatHistory") if chatHistoryIndex >= 0 { // 在 chatHistory 标记位置注入原始消息 result = append(result, messages...) } else { // 如果没有 chatHistory 标记,直接添加到末尾 result = append(result, messages...) } // 3. 添加其他角色的提示词(assistant等) for _, prompt := range otherPrompts { result = append(result, request.ChatMessage{ Role: prompt.Role, Content: p.processPromptContent(prompt.Content), }) } return result } // findMarkerIndex 查找标记位置 func (p *PresetInjector) findMarkerIndex(identifier string) int { for i, prompt := range p.preset.Prompts { if prompt.Identifier == identifier && prompt.Marker { return i } } return -1 } // processPromptContent 处理提示词内容(变量替换等) func (p *PresetInjector) processPromptContent(content string) string { // 处理 {{user}} 和 {{char}} 等变量 content = strings.ReplaceAll(content, "{{user}}", "User") content = strings.ReplaceAll(content, "{{char}}", "Assistant") // 处理 {{getvar::key}} 语法 getvarRegex := regexp.MustCompile(`\{\{getvar::(\w+)\}\}`) content = getvarRegex.ReplaceAllString(content, "") // 处理 {{setvar::key::value}} 语法 setvarRegex := regexp.MustCompile(`\{\{setvar::(\w+)::(.*?)\}\}`) content = setvarRegex.ReplaceAllString(content, "") // 处理注释 {{//...}} commentRegex := regexp.MustCompile(`\{\{//.*?\}\}`) content = commentRegex.ReplaceAllString(content, "") return strings.TrimSpace(content) } // applyRegexScripts 应用正则替换脚本 func (p *PresetInjector) applyRegexScripts(messages []request.ChatMessage, placement int) []request.ChatMessage { if p.preset.Extensions.RegexBinding == nil { return messages } for _, script := range p.preset.Extensions.RegexBinding.Regexes { if script.Disabled { continue } // 检查 placement hasPlacement := false for _, p := range script.Placement { if p == placement { hasPlacement = true break } } if !hasPlacement { continue } // 应用正则替换 messages = p.applyRegexScript(messages, script) } return messages } // applyRegexScript 应用单个正则脚本 func (p *PresetInjector) applyRegexScript(messages []request.ChatMessage, script app.RegexScript) []request.ChatMessage { // 解析正则表达式 pattern := script.FindRegex // 移除正则标志(如 /pattern/g) if strings.HasPrefix(pattern, "/") && strings.HasSuffix(pattern, "/g") { pattern = pattern[1 : len(pattern)-2] } else if strings.HasPrefix(pattern, "/") { lastSlash := strings.LastIndex(pattern, "/") if lastSlash > 0 { pattern = pattern[1:lastSlash] } } re, err := regexp.Compile(pattern) if err != nil { return messages } // 对每条消息应用替换 for i := range messages { if script.PromptOnly && messages[i].Role != "user" { continue } messages[i].Content = re.ReplaceAllString(messages[i].Content, script.ReplaceString) } return messages } // ProcessResponse 处理AI响应(应用输出后的正则) func (p *PresetInjector) ProcessResponse(content string) string { if p.preset == nil || p.preset.Extensions.RegexBinding == nil { return content } for _, script := range p.preset.Extensions.RegexBinding.Regexes { if script.Disabled { continue } // 检查是否应用于输出(placement=2) hasPlacement := false for _, placement := range script.Placement { if placement == 2 { hasPlacement = true break } } if !hasPlacement { continue } // 解析正则表达式 pattern := script.FindRegex if strings.HasPrefix(pattern, "/") && strings.HasSuffix(pattern, "/g") { pattern = pattern[1 : len(pattern)-2] } else if strings.HasPrefix(pattern, "/") { lastSlash := strings.LastIndex(pattern, "/") if lastSlash > 0 { pattern = pattern[1:lastSlash] } } re, err := regexp.Compile(pattern) if err != nil { continue } content = re.ReplaceAllString(content, script.ReplaceString) } return content } // ApplyPresetParameters 应用预设参数到请求 func (p *PresetInjector) ApplyPresetParameters(req *request.ChatCompletionRequest) { if p.preset == nil { return } // 如果请求中没有指定参数,使用预设的参数 if req.Temperature == nil && p.preset.Temperature > 0 { temp := p.preset.Temperature req.Temperature = &temp } if req.TopP == nil && p.preset.TopP > 0 { topP := p.preset.TopP req.TopP = &topP } if req.MaxTokens == nil && p.preset.MaxTokens > 0 { maxTokens := p.preset.MaxTokens req.MaxTokens = &maxTokens } if req.PresencePenalty == nil && p.preset.PresencePenalty != 0 { pp := p.preset.PresencePenalty req.PresencePenalty = &pp } if req.FrequencyPenalty == nil && p.preset.FrequencyPenalty != 0 { fp := p.preset.FrequencyPenalty req.FrequencyPenalty = &fp } } // ValidatePreset 验证预设配置 func ValidatePreset(preset *app.AiPreset) error { if preset == nil { return fmt.Errorf("预设不能为空") } if preset.Name == "" { return fmt.Errorf("预设名称不能为空") } // 验证正则表达式 if preset.Extensions.RegexBinding != nil { for _, script := range preset.Extensions.RegexBinding.Regexes { pattern := script.FindRegex if strings.HasPrefix(pattern, "/") { lastSlash := strings.LastIndex(pattern, "/") if lastSlash > 0 { pattern = pattern[1:lastSlash] } } _, err := regexp.Compile(pattern) if err != nil { return fmt.Errorf("正则表达式 '%s' 无效: %v", script.ScriptName, err) } } } return nil }