🎨 优化预设正则解析
This commit is contained in:
@@ -26,7 +26,8 @@ func (s *AiModelService) DeleteAiModel(id uint, userID uint) error {
|
||||
|
||||
// UpdateAiModel 更新模型
|
||||
func (s *AiModelService) UpdateAiModel(model *app.AiModel, userID uint) error {
|
||||
return global.GVA_DB.Where("user_id = ?", userID).Updates(model).Error
|
||||
// 使用 Select("*") 来更新所有字段,包括零值字段(如 enabled=false)
|
||||
return global.GVA_DB.Model(&app.AiModel{}).Where("id = ? AND user_id = ?", model.ID, userID).Select("*").Updates(model).Error
|
||||
}
|
||||
|
||||
// GetAiModel 查询模型
|
||||
|
||||
@@ -46,7 +46,8 @@ func (s *AiPresetService) GetAiPresetList(info request.PageInfo, userID uint) (l
|
||||
}
|
||||
|
||||
// ParseImportedPreset 解析导入的预设,支持 SillyTavern 格式
|
||||
func (s *AiPresetService) ParseImportedPreset(rawData map[string]interface{}) (*app.AiPreset, error) {
|
||||
// defaultName: 当 JSON 中没有名称时使用的默认名称(通常是文件名)
|
||||
func (s *AiPresetService) ParseImportedPreset(rawData map[string]interface{}, defaultName string) (*app.AiPreset, error) {
|
||||
preset := &app.AiPreset{
|
||||
Enabled: true,
|
||||
}
|
||||
@@ -58,6 +59,9 @@ func (s *AiPresetService) ParseImportedPreset(rawData map[string]interface{}) (*
|
||||
preset.Name = name
|
||||
} else if name, ok := rawData["presetName"].(string); ok && name != "" {
|
||||
preset.Name = name
|
||||
} else if defaultName != "" {
|
||||
// 使用默认名称(文件名)
|
||||
preset.Name = defaultName
|
||||
} else {
|
||||
return nil, fmt.Errorf("预设名称不能为空")
|
||||
}
|
||||
@@ -110,7 +114,8 @@ func (s *AiPresetService) ParseImportedPreset(rawData map[string]interface{}) (*
|
||||
json.Unmarshal(orderData, &preset.PromptOrder)
|
||||
}
|
||||
|
||||
// 处理正则脚本
|
||||
// 处理正则脚本 - 支持两种格式
|
||||
// 格式1: 顶层 regex_scripts
|
||||
if regexScripts, ok := rawData["regex_scripts"].([]interface{}); ok {
|
||||
scriptsData, _ := json.Marshal(regexScripts)
|
||||
json.Unmarshal(scriptsData, &preset.RegexScripts)
|
||||
@@ -118,6 +123,20 @@ func (s *AiPresetService) ParseImportedPreset(rawData map[string]interface{}) (*
|
||||
|
||||
// 处理扩展配置
|
||||
if extensions, ok := rawData["extensions"].(map[string]interface{}); ok {
|
||||
// 格式2: extensions.regex_scripts (SillyTavern 格式)
|
||||
if regexScripts, ok := extensions["regex_scripts"].([]interface{}); ok {
|
||||
scriptsData, _ := json.Marshal(regexScripts)
|
||||
// 同时填充到 RegexScripts 和 Extensions.RegexBinding.Regexes
|
||||
json.Unmarshal(scriptsData, &preset.RegexScripts)
|
||||
|
||||
// 确保 Extensions.RegexBinding 被初始化
|
||||
if preset.Extensions.RegexBinding == nil {
|
||||
preset.Extensions.RegexBinding = &app.RegexBindingConfig{}
|
||||
}
|
||||
json.Unmarshal(scriptsData, &preset.Extensions.RegexBinding.Regexes)
|
||||
}
|
||||
|
||||
// 解析其他扩展配置
|
||||
extData, _ := json.Marshal(extensions)
|
||||
json.Unmarshal(extData, &preset.Extensions)
|
||||
}
|
||||
|
||||
@@ -8,16 +8,29 @@ import (
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/model/app"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/app/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/app/response"
|
||||
)
|
||||
|
||||
// PresetInjector 预设注入器
|
||||
type PresetInjector struct {
|
||||
preset *app.AiPreset
|
||||
preset *app.AiPreset
|
||||
regexLogs *response.RegexExecutionLogs
|
||||
}
|
||||
|
||||
// NewPresetInjector 创建预设注入器
|
||||
func NewPresetInjector(preset *app.AiPreset) *PresetInjector {
|
||||
return &PresetInjector{preset: preset}
|
||||
return &PresetInjector{
|
||||
preset: preset,
|
||||
regexLogs: &response.RegexExecutionLogs{
|
||||
InputScripts: []response.RegexScriptLog{},
|
||||
OutputScripts: []response.RegexScriptLog{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetRegexLogs 获取正则脚本执行日志
|
||||
func (p *PresetInjector) GetRegexLogs() *response.RegexExecutionLogs {
|
||||
return p.regexLogs
|
||||
}
|
||||
|
||||
// InjectMessages 注入预设到消息列表
|
||||
@@ -196,8 +209,8 @@ func (p *PresetInjector) applyRegexScripts(messages []request.ChatMessage, place
|
||||
|
||||
// 检查 placement
|
||||
hasPlacement := false
|
||||
for _, p := range script.Placement {
|
||||
if p == placement {
|
||||
for _, pl := range script.Placement {
|
||||
if pl == placement {
|
||||
hasPlacement = true
|
||||
break
|
||||
}
|
||||
@@ -206,15 +219,34 @@ func (p *PresetInjector) applyRegexScripts(messages []request.ChatMessage, place
|
||||
continue
|
||||
}
|
||||
|
||||
// 应用正则替换
|
||||
messages = p.applyRegexScript(messages, script)
|
||||
// 应用正则替换并记录日志
|
||||
var matchCount int
|
||||
var err error
|
||||
messages, matchCount, err = p.applyRegexScriptWithLog(messages, script)
|
||||
|
||||
// 记录执行日志
|
||||
log := response.RegexScriptLog{
|
||||
ScriptName: script.ScriptName,
|
||||
ScriptID: script.ID,
|
||||
Executed: true,
|
||||
MatchCount: matchCount,
|
||||
}
|
||||
if err != nil {
|
||||
log.ErrorMessage = err.Error()
|
||||
}
|
||||
|
||||
// 根据 placement 添加到对应的日志列表
|
||||
if placement == 1 {
|
||||
p.regexLogs.InputScripts = append(p.regexLogs.InputScripts, log)
|
||||
}
|
||||
p.regexLogs.TotalMatches += matchCount
|
||||
}
|
||||
|
||||
return messages
|
||||
}
|
||||
|
||||
// applyRegexScript 应用单个正则脚本
|
||||
func (p *PresetInjector) applyRegexScript(messages []request.ChatMessage, script app.RegexScript) []request.ChatMessage {
|
||||
// applyRegexScriptWithLog 应用单个正则脚本并返回匹配次数
|
||||
func (p *PresetInjector) applyRegexScriptWithLog(messages []request.ChatMessage, script app.RegexScript) ([]request.ChatMessage, int, error) {
|
||||
// 解析正则表达式
|
||||
pattern := script.FindRegex
|
||||
// 移除正则标志(如 /pattern/g)
|
||||
@@ -229,19 +261,25 @@ func (p *PresetInjector) applyRegexScript(messages []request.ChatMessage, script
|
||||
|
||||
re, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return messages
|
||||
return messages, 0, fmt.Errorf("正则编译失败: %v", err)
|
||||
}
|
||||
|
||||
matchCount := 0
|
||||
// 对每条消息应用替换
|
||||
for i := range messages {
|
||||
if script.PromptOnly && messages[i].Role != "user" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 统计匹配次数
|
||||
matches := re.FindAllString(messages[i].Content, -1)
|
||||
matchCount += len(matches)
|
||||
|
||||
// 执行替换
|
||||
messages[i].Content = re.ReplaceAllString(messages[i].Content, script.ReplaceString)
|
||||
}
|
||||
|
||||
return messages
|
||||
return messages, matchCount, nil
|
||||
}
|
||||
|
||||
// ProcessResponse 处理AI响应(应用输出后的正则)
|
||||
@@ -280,10 +318,31 @@ func (p *PresetInjector) ProcessResponse(content string) string {
|
||||
|
||||
re, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
// 记录错误日志
|
||||
p.regexLogs.OutputScripts = append(p.regexLogs.OutputScripts, response.RegexScriptLog{
|
||||
ScriptName: script.ScriptName,
|
||||
ScriptID: script.ID,
|
||||
Executed: false,
|
||||
ErrorMessage: fmt.Sprintf("正则编译失败: %v", err),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 统计匹配次数
|
||||
matches := re.FindAllString(content, -1)
|
||||
matchCount := len(matches)
|
||||
|
||||
// 执行替换
|
||||
content = re.ReplaceAllString(content, script.ReplaceString)
|
||||
|
||||
// 记录执行日志
|
||||
p.regexLogs.OutputScripts = append(p.regexLogs.OutputScripts, response.RegexScriptLog{
|
||||
ScriptName: script.ScriptName,
|
||||
ScriptID: script.ID,
|
||||
Executed: true,
|
||||
MatchCount: matchCount,
|
||||
})
|
||||
p.regexLogs.TotalMatches += matchCount
|
||||
}
|
||||
|
||||
return content
|
||||
|
||||
@@ -34,8 +34,9 @@ func (s *AiProxyService) ProcessChatCompletion(ctx context.Context, req *request
|
||||
}
|
||||
|
||||
// 2. 注入预设
|
||||
var injector *PresetInjector
|
||||
if preset != nil {
|
||||
injector := NewPresetInjector(preset)
|
||||
injector = NewPresetInjector(preset)
|
||||
req.Messages = injector.InjectMessages(req.Messages)
|
||||
injector.ApplyPresetParameters(req)
|
||||
}
|
||||
@@ -46,10 +47,15 @@ func (s *AiProxyService) ProcessChatCompletion(ctx context.Context, req *request
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 4. 处理响应
|
||||
if preset != nil && len(resp.Choices) > 0 {
|
||||
injector := NewPresetInjector(preset)
|
||||
// 4. 处理响应并收集正则日志
|
||||
if preset != nil && injector != nil && len(resp.Choices) > 0 {
|
||||
resp.Choices[0].Message.Content = injector.ProcessResponse(resp.Choices[0].Message.Content)
|
||||
|
||||
// 添加正则执行日志到响应
|
||||
regexLogs := injector.GetRegexLogs()
|
||||
if regexLogs.TotalMatches > 0 || len(regexLogs.InputScripts) > 0 || len(regexLogs.OutputScripts) > 0 {
|
||||
resp.RegexLogs = regexLogs
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
||||
Reference in New Issue
Block a user