🎨 优化预设 && 优化对话是预设和模型切换

Signed-off-by: Echo <1711788888@qq.com>
This commit is contained in:
2026-02-28 15:09:40 +08:00
parent 032d0ccdf0
commit 81b552b689
7 changed files with 113 additions and 50 deletions

View File

@@ -132,7 +132,7 @@ func (a *ConversationApi) UpdateConversationSettings(c *gin.Context) {
return return
} }
err = service.ServiceGroupApp.AppServiceGroup.ConversationService.UpdateConversationSettings(userID, uint(conversationID), req.Settings) err = service.ServiceGroupApp.AppServiceGroup.ConversationService.UpdateConversationSettings(userID, uint(conversationID), &req)
if err != nil { if err != nil {
global.GVA_LOG.Error("更新对话设置失败", zap.Error(err)) global.GVA_LOG.Error("更新对话设置失败", zap.Error(err))
commonResponse.FailWithMessage(err.Error(), c) commonResponse.FailWithMessage(err.Error(), c)

View File

@@ -19,10 +19,12 @@ type Conversation struct {
Title string `gorm:"type:varchar(200)" json:"title"` // 对话标题 Title string `gorm:"type:varchar(200)" json:"title"` // 对话标题
// 对话配置 // 对话配置
PresetID *uint `gorm:"index" json:"presetId"` // 使用的预设ID PresetID *uint `gorm:"index" json:"presetId"` // 使用的预设ID
AIProvider string `gorm:"type:varchar(50)" json:"aiProvider"` // AI提供商 (openai/anthropic) WorldbookID *uint `gorm:"index" json:"worldbookId"` // 使用的世界书ID
Model string `gorm:"type:varchar(100)" json:"model"` // 使用的模型 AIProvider string `gorm:"type:varchar(50)" json:"aiProvider"` // AI提供商 (openai/anthropic)
Settings datatypes.JSON `gorm:"type:jsonb" json:"settings"` // 对话设置 (temperature等) Model string `gorm:"type:varchar(100)" json:"model"` // 使用的模型
Settings datatypes.JSON `gorm:"type:jsonb" json:"settings"` // 对话设置 (temperature等)
WorldbookEnabled bool `gorm:"default:false" json:"worldbookEnabled"` // 是否启用世界书
// 统计信息 // 统计信息
MessageCount int `gorm:"default:0" json:"messageCount"` // 消息数量 MessageCount int `gorm:"default:0" json:"messageCount"` // 消息数量

View File

@@ -2,11 +2,13 @@ package request
// CreateConversationRequest 创建对话请求 // CreateConversationRequest 创建对话请求
type CreateConversationRequest struct { type CreateConversationRequest struct {
CharacterID uint `json:"characterId" binding:"required"` CharacterID uint `json:"characterId" binding:"required"`
Title string `json:"title" binding:"max=200"` Title string `json:"title" binding:"max=200"`
PresetID *uint `json:"presetId"` PresetID *uint `json:"presetId"`
AIProvider string `json:"aiProvider" binding:"omitempty,oneof=openai anthropic"` WorldbookID *uint `json:"worldbookId"`
Model string `json:"model"` WorldbookEnabled bool `json:"worldbookEnabled"`
AIProvider string `json:"aiProvider" binding:"omitempty,oneof=openai anthropic"`
Model string `json:"model"`
} }
// SendMessageRequest 发送消息请求 // SendMessageRequest 发送消息请求
@@ -28,5 +30,7 @@ type GetMessageListRequest struct {
// UpdateConversationSettingsRequest 更新对话设置请求 // UpdateConversationSettingsRequest 更新对话设置请求
type UpdateConversationSettingsRequest struct { type UpdateConversationSettingsRequest struct {
Settings map[string]interface{} `json:"settings" binding:"required"` Settings map[string]interface{} `json:"settings"`
WorldbookID *uint `json:"worldbookId"`
WorldbookEnabled *bool `json:"worldbookEnabled"`
} }

View File

@@ -9,17 +9,19 @@ import (
// ConversationResponse 对话响应 // ConversationResponse 对话响应
type ConversationResponse struct { type ConversationResponse struct {
ID uint `json:"id"` ID uint `json:"id"`
CharacterID uint `json:"characterId"` CharacterID uint `json:"characterId"`
Title string `json:"title"` Title string `json:"title"`
PresetID *uint `json:"presetId"` PresetID *uint `json:"presetId"`
AIProvider string `json:"aiProvider"` WorldbookID *uint `json:"worldbookId"`
Model string `json:"model"` WorldbookEnabled bool `json:"worldbookEnabled"`
Settings map[string]interface{} `json:"settings,omitempty"` AIProvider string `json:"aiProvider"`
MessageCount int `json:"messageCount"` Model string `json:"model"`
TokenCount int `json:"tokenCount"` Settings map[string]interface{} `json:"settings,omitempty"`
CreatedAt time.Time `json:"createdAt"` MessageCount int `json:"messageCount"`
UpdatedAt time.Time `json:"updatedAt"` TokenCount int `json:"tokenCount"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
// 关联数据 // 关联数据
Character *CharacterResponse `json:"character,omitempty"` Character *CharacterResponse `json:"character,omitempty"`
@@ -75,17 +77,19 @@ func ToConversationResponse(conv *app.Conversation) ConversationResponse {
} }
return ConversationResponse{ return ConversationResponse{
ID: conv.ID, ID: conv.ID,
CharacterID: conv.CharacterID, CharacterID: conv.CharacterID,
Title: conv.Title, Title: conv.Title,
PresetID: conv.PresetID, PresetID: conv.PresetID,
AIProvider: conv.AIProvider, WorldbookID: conv.WorldbookID,
Model: conv.Model, WorldbookEnabled: conv.WorldbookEnabled,
Settings: settings, AIProvider: conv.AIProvider,
MessageCount: conv.MessageCount, Model: conv.Model,
TokenCount: conv.TokenCount, Settings: settings,
CreatedAt: conv.CreatedAt, MessageCount: conv.MessageCount,
UpdatedAt: conv.UpdatedAt, TokenCount: conv.TokenCount,
CreatedAt: conv.CreatedAt,
UpdatedAt: conv.UpdatedAt,
} }
} }

View File

@@ -72,13 +72,15 @@ func (s *ConversationService) CreateConversation(userID uint, req *request.Creat
// 创建对话 // 创建对话
conversation := app.Conversation{ conversation := app.Conversation{
UserID: userID, UserID: userID,
CharacterID: req.CharacterID, CharacterID: req.CharacterID,
Title: title, Title: title,
PresetID: req.PresetID, PresetID: req.PresetID,
AIProvider: aiProvider, WorldbookID: req.WorldbookID,
Model: model, WorldbookEnabled: req.WorldbookEnabled,
Settings: datatypes.JSON("{}"), AIProvider: aiProvider,
Model: model,
Settings: datatypes.JSON("{}"),
} }
err = global.GVA_DB.Create(&conversation).Error err = global.GVA_DB.Create(&conversation).Error
@@ -188,7 +190,7 @@ func (s *ConversationService) GetConversationByID(userID, conversationID uint) (
} }
// UpdateConversationSettings 更新对话设置 // UpdateConversationSettings 更新对话设置
func (s *ConversationService) UpdateConversationSettings(userID, conversationID uint, settings map[string]interface{}) error { func (s *ConversationService) UpdateConversationSettings(userID, conversationID uint, req *request.UpdateConversationSettingsRequest) error {
var conversation app.Conversation var conversation app.Conversation
err := global.GVA_DB.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error err := global.GVA_DB.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error
if err != nil { if err != nil {
@@ -198,13 +200,32 @@ func (s *ConversationService) UpdateConversationSettings(userID, conversationID
return err return err
} }
// 序列化设置 updates := make(map[string]interface{})
settingsJSON, err := json.Marshal(settings)
if err != nil { // 更新设置
return err if req.Settings != nil {
settingsJSON, err := json.Marshal(req.Settings)
if err != nil {
return err
}
updates["settings"] = datatypes.JSON(settingsJSON)
} }
return global.GVA_DB.Model(&conversation).Update("settings", datatypes.JSON(settingsJSON)).Error // 更新世界书ID
if req.WorldbookID != nil {
updates["worldbook_id"] = req.WorldbookID
}
// 更新世界书启用状态
if req.WorldbookEnabled != nil {
updates["worldbook_enabled"] = *req.WorldbookEnabled
}
if len(updates) == 0 {
return nil
}
return global.GVA_DB.Model(&conversation).Updates(updates).Error
} }
// DeleteConversation 删除对话 // DeleteConversation 删除对话
@@ -431,6 +452,30 @@ func (s *ConversationService) callAIService(conversation app.Conversation, chara
global.GVA_LOG.Info("已追加预设的系统提示词") global.GVA_LOG.Info("已追加预设的系统提示词")
} }
// 集成世界书触发引擎
if conversation.WorldbookEnabled && conversation.WorldbookID != nil {
global.GVA_LOG.Info(fmt.Sprintf("世界书已启用ID: %d", *conversation.WorldbookID))
// 提取消息内容用于扫描
var messageContents []string
for _, msg := range messages {
messageContents = append(messageContents, msg.Content)
}
// 使用世界书引擎扫描并触发条目
engine := &WorldbookEngine{}
triggered, err := engine.ScanAndTrigger(*conversation.WorldbookID, messageContents)
if err != nil {
global.GVA_LOG.Warn(fmt.Sprintf("世界书触发失败: %v", err))
} else if len(triggered) > 0 {
global.GVA_LOG.Info(fmt.Sprintf("触发了 %d 个世界书条目", len(triggered)))
// 将触发的世界书内容注入到系统提示词
systemPrompt = engine.BuildPromptWithWorldbook(systemPrompt, triggered)
} else {
global.GVA_LOG.Info("没有触发任何世界书条目")
}
}
// 构建消息列表 // 构建消息列表
apiMessages := s.buildAPIMessages(messages, systemPrompt) apiMessages := s.buildAPIMessages(messages, systemPrompt)

View File

@@ -28,6 +28,8 @@ export interface Conversation {
characterId: number characterId: number
title: string title: string
presetId?: number presetId?: number
worldbookId?: number
worldbookEnabled: boolean
aiProvider: string aiProvider: string
model: string model: string
settings?: Record<string, any> settings?: Record<string, any>
@@ -50,6 +52,8 @@ export interface CreateConversationRequest {
characterId: number characterId: number
title?: string title?: string
presetId?: number presetId?: number
worldbookId?: number
worldbookEnabled?: boolean
aiProvider?: string aiProvider?: string
model?: string model?: string
settings?: Record<string, any> settings?: Record<string, any>
@@ -103,8 +107,12 @@ export const conversationApi = {
}, },
// 更新对话设置 // 更新对话设置
updateConversationSettings: (conversationId: number, settings: Record<string, any>) => { updateConversationSettings: (conversationId: number, data: {
return apiClient.put(`/app/conversation/${conversationId}/settings`, { settings }) settings?: Record<string, any>
worldbookId?: number
worldbookEnabled?: boolean
}) => {
return apiClient.put(`/app/conversation/${conversationId}/settings`, data)
}, },
// 重新生成最后一条 AI 回复(非流式) // 重新生成最后一条 AI 回复(非流式)

View File

@@ -149,7 +149,7 @@ export default function ChatArea({ conversation, character, onConversationUpdate
} else { } else {
settings.presetId = presetId settings.presetId = presetId
} }
await conversationApi.updateConversationSettings(conversation.id, settings) await conversationApi.updateConversationSettings(conversation.id, { settings })
setSelectedPresetId(presetId ?? undefined) setSelectedPresetId(presetId ?? undefined)
setShowPresetSelector(false) setShowPresetSelector(false)
const convResp = await conversationApi.getConversationById(conversation.id) const convResp = await conversationApi.getConversationById(conversation.id)
@@ -170,7 +170,7 @@ export default function ChatArea({ conversation, character, onConversationUpdate
} else { } else {
settings.aiConfigId = configId settings.aiConfigId = configId
} }
await conversationApi.updateConversationSettings(conversation.id, settings) await conversationApi.updateConversationSettings(conversation.id, { settings })
setSelectedConfigId(configId ?? undefined) setSelectedConfigId(configId ?? undefined)
setShowModelSelector(false) setShowModelSelector(false)
const convResp = await conversationApi.getConversationById(conversation.id) const convResp = await conversationApi.getConversationById(conversation.id)