diff --git a/server/api/v1/app/conversation.go b/server/api/v1/app/conversation.go index 99a3ba6..a424890 100644 --- a/server/api/v1/app/conversation.go +++ b/server/api/v1/app/conversation.go @@ -132,7 +132,7 @@ func (a *ConversationApi) UpdateConversationSettings(c *gin.Context) { 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 { global.GVA_LOG.Error("更新对话设置失败", zap.Error(err)) commonResponse.FailWithMessage(err.Error(), c) diff --git a/server/model/app/conversation.go b/server/model/app/conversation.go index 1e3128f..98e06b8 100644 --- a/server/model/app/conversation.go +++ b/server/model/app/conversation.go @@ -19,10 +19,12 @@ type Conversation struct { Title string `gorm:"type:varchar(200)" json:"title"` // 对话标题 // 对话配置 - PresetID *uint `gorm:"index" json:"presetId"` // 使用的预设ID - AIProvider string `gorm:"type:varchar(50)" json:"aiProvider"` // AI提供商 (openai/anthropic) - Model string `gorm:"type:varchar(100)" json:"model"` // 使用的模型 - Settings datatypes.JSON `gorm:"type:jsonb" json:"settings"` // 对话设置 (temperature等) + PresetID *uint `gorm:"index" json:"presetId"` // 使用的预设ID + WorldbookID *uint `gorm:"index" json:"worldbookId"` // 使用的世界书ID + AIProvider string `gorm:"type:varchar(50)" json:"aiProvider"` // AI提供商 (openai/anthropic) + 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"` // 消息数量 diff --git a/server/model/app/request/conversation.go b/server/model/app/request/conversation.go index a179690..be57afe 100644 --- a/server/model/app/request/conversation.go +++ b/server/model/app/request/conversation.go @@ -2,11 +2,13 @@ package request // CreateConversationRequest 创建对话请求 type CreateConversationRequest struct { - CharacterID uint `json:"characterId" binding:"required"` - Title string `json:"title" binding:"max=200"` - PresetID *uint `json:"presetId"` - AIProvider string `json:"aiProvider" binding:"omitempty,oneof=openai anthropic"` - Model string `json:"model"` + CharacterID uint `json:"characterId" binding:"required"` + Title string `json:"title" binding:"max=200"` + PresetID *uint `json:"presetId"` + WorldbookID *uint `json:"worldbookId"` + WorldbookEnabled bool `json:"worldbookEnabled"` + AIProvider string `json:"aiProvider" binding:"omitempty,oneof=openai anthropic"` + Model string `json:"model"` } // SendMessageRequest 发送消息请求 @@ -28,5 +30,7 @@ type GetMessageListRequest struct { // UpdateConversationSettingsRequest 更新对话设置请求 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"` } diff --git a/server/model/app/response/conversation.go b/server/model/app/response/conversation.go index 858f5d9..7ba07c0 100644 --- a/server/model/app/response/conversation.go +++ b/server/model/app/response/conversation.go @@ -9,17 +9,19 @@ import ( // ConversationResponse 对话响应 type ConversationResponse struct { - ID uint `json:"id"` - CharacterID uint `json:"characterId"` - Title string `json:"title"` - PresetID *uint `json:"presetId"` - AIProvider string `json:"aiProvider"` - Model string `json:"model"` - Settings map[string]interface{} `json:"settings,omitempty"` - MessageCount int `json:"messageCount"` - TokenCount int `json:"tokenCount"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID uint `json:"id"` + CharacterID uint `json:"characterId"` + Title string `json:"title"` + PresetID *uint `json:"presetId"` + WorldbookID *uint `json:"worldbookId"` + WorldbookEnabled bool `json:"worldbookEnabled"` + AIProvider string `json:"aiProvider"` + Model string `json:"model"` + Settings map[string]interface{} `json:"settings,omitempty"` + MessageCount int `json:"messageCount"` + TokenCount int `json:"tokenCount"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` // 关联数据 Character *CharacterResponse `json:"character,omitempty"` @@ -75,17 +77,19 @@ func ToConversationResponse(conv *app.Conversation) ConversationResponse { } return ConversationResponse{ - ID: conv.ID, - CharacterID: conv.CharacterID, - Title: conv.Title, - PresetID: conv.PresetID, - AIProvider: conv.AIProvider, - Model: conv.Model, - Settings: settings, - MessageCount: conv.MessageCount, - TokenCount: conv.TokenCount, - CreatedAt: conv.CreatedAt, - UpdatedAt: conv.UpdatedAt, + ID: conv.ID, + CharacterID: conv.CharacterID, + Title: conv.Title, + PresetID: conv.PresetID, + WorldbookID: conv.WorldbookID, + WorldbookEnabled: conv.WorldbookEnabled, + AIProvider: conv.AIProvider, + Model: conv.Model, + Settings: settings, + MessageCount: conv.MessageCount, + TokenCount: conv.TokenCount, + CreatedAt: conv.CreatedAt, + UpdatedAt: conv.UpdatedAt, } } diff --git a/server/service/app/conversation.go b/server/service/app/conversation.go index 8469f5d..cd27694 100644 --- a/server/service/app/conversation.go +++ b/server/service/app/conversation.go @@ -72,13 +72,15 @@ func (s *ConversationService) CreateConversation(userID uint, req *request.Creat // 创建对话 conversation := app.Conversation{ - UserID: userID, - CharacterID: req.CharacterID, - Title: title, - PresetID: req.PresetID, - AIProvider: aiProvider, - Model: model, - Settings: datatypes.JSON("{}"), + UserID: userID, + CharacterID: req.CharacterID, + Title: title, + PresetID: req.PresetID, + WorldbookID: req.WorldbookID, + WorldbookEnabled: req.WorldbookEnabled, + AIProvider: aiProvider, + Model: model, + Settings: datatypes.JSON("{}"), } err = global.GVA_DB.Create(&conversation).Error @@ -188,7 +190,7 @@ func (s *ConversationService) GetConversationByID(userID, conversationID uint) ( } // 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 err := global.GVA_DB.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error if err != nil { @@ -198,13 +200,32 @@ func (s *ConversationService) UpdateConversationSettings(userID, conversationID return err } - // 序列化设置 - settingsJSON, err := json.Marshal(settings) - if err != nil { - return err + updates := make(map[string]interface{}) + + // 更新设置 + 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 删除对话 @@ -431,6 +452,30 @@ func (s *ConversationService) callAIService(conversation app.Conversation, chara 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) diff --git a/web-app/src/api/conversation.ts b/web-app/src/api/conversation.ts index 9d05e30..ef817d3 100644 --- a/web-app/src/api/conversation.ts +++ b/web-app/src/api/conversation.ts @@ -28,6 +28,8 @@ export interface Conversation { characterId: number title: string presetId?: number + worldbookId?: number + worldbookEnabled: boolean aiProvider: string model: string settings?: Record @@ -50,6 +52,8 @@ export interface CreateConversationRequest { characterId: number title?: string presetId?: number + worldbookId?: number + worldbookEnabled?: boolean aiProvider?: string model?: string settings?: Record @@ -103,8 +107,12 @@ export const conversationApi = { }, // 更新对话设置 - updateConversationSettings: (conversationId: number, settings: Record) => { - return apiClient.put(`/app/conversation/${conversationId}/settings`, { settings }) + updateConversationSettings: (conversationId: number, data: { + settings?: Record + worldbookId?: number + worldbookEnabled?: boolean + }) => { + return apiClient.put(`/app/conversation/${conversationId}/settings`, data) }, // 重新生成最后一条 AI 回复(非流式) diff --git a/web-app/src/components/ChatArea.tsx b/web-app/src/components/ChatArea.tsx index d0e2a3e..6b43c40 100644 --- a/web-app/src/components/ChatArea.tsx +++ b/web-app/src/components/ChatArea.tsx @@ -149,7 +149,7 @@ export default function ChatArea({ conversation, character, onConversationUpdate } else { settings.presetId = presetId } - await conversationApi.updateConversationSettings(conversation.id, settings) + await conversationApi.updateConversationSettings(conversation.id, { settings }) setSelectedPresetId(presetId ?? undefined) setShowPresetSelector(false) const convResp = await conversationApi.getConversationById(conversation.id) @@ -170,7 +170,7 @@ export default function ChatArea({ conversation, character, onConversationUpdate } else { settings.aiConfigId = configId } - await conversationApi.updateConversationSettings(conversation.id, settings) + await conversationApi.updateConversationSettings(conversation.id, { settings }) setSelectedConfigId(configId ?? undefined) setShowModelSelector(false) const convResp = await conversationApi.getConversationById(conversation.id)