✨ 新增世界书模块
This commit is contained in:
@@ -9,18 +9,57 @@ import (
|
||||
// AIWorldInfo 世界书(World Info)表
|
||||
type AIWorldInfo struct {
|
||||
global.GVA_MODEL
|
||||
UserID uint `json:"userId" gorm:"not null;index;comment:所属用户ID"`
|
||||
User *AppUser `json:"user" gorm:"foreignKey:UserID"`
|
||||
CharacterID *uint `json:"characterId" gorm:"index;comment:关联角色ID"`
|
||||
Character *AICharacter `json:"character" gorm:"foreignKey:CharacterID"`
|
||||
Name string `json:"name" gorm:"type:varchar(500);not null;comment:世界书名称"`
|
||||
Keywords pq.StringArray `json:"keywords" gorm:"type:text[];comment:触发关键词"`
|
||||
Content string `json:"content" gorm:"type:text;not null;comment:内容"`
|
||||
Priority int `json:"priority" gorm:"default:0;comment:优先级"`
|
||||
IsEnabled bool `json:"isEnabled" gorm:"default:true;comment:是否启用"`
|
||||
TriggerConfig datatypes.JSON `json:"triggerConfig" gorm:"type:jsonb;comment:触发条件配置"`
|
||||
UserID uint `json:"userId" gorm:"not null;index;comment:所属用户ID"`
|
||||
User *AppUser `json:"user" gorm:"foreignKey:UserID"`
|
||||
BookName string `json:"bookName" gorm:"type:varchar(500);not null;index;comment:世界书名称"`
|
||||
IsGlobal bool `json:"isGlobal" gorm:"default:false;index;comment:是否全局世界书"`
|
||||
Entries datatypes.JSON `json:"entries" gorm:"type:jsonb;not null;comment:世界书条目列表"`
|
||||
LinkedChars pq.StringArray `json:"linkedChars" gorm:"type:text[];comment:关联的角色ID列表"`
|
||||
}
|
||||
|
||||
func (AIWorldInfo) TableName() string {
|
||||
return "ai_world_info"
|
||||
}
|
||||
|
||||
// AIWorldInfoEntry 世界书条目(存储在 AIWorldInfo.Entries 中)
|
||||
type AIWorldInfoEntry struct {
|
||||
UID string `json:"uid"` // 条目唯一ID
|
||||
Keys []string `json:"keys"` // 主要关键词
|
||||
SecondaryKeys []string `json:"secondary_keys"` // 次要关键词(可选匹配)
|
||||
Content string `json:"content"` // 条目内容
|
||||
Comment string `json:"comment"` // 备注
|
||||
Enabled bool `json:"enabled"` // 是否启用
|
||||
Constant bool `json:"constant"` // 永远激活
|
||||
Selective bool `json:"selective"` // 选择性激活(需要主键+次键都匹配)
|
||||
Order int `json:"order"` // 插入顺序(越小越靠前)
|
||||
Position string `json:"position"` // 插入位置:before_char, after_char
|
||||
Depth int `json:"depth"` // 扫描深度(从最近消息往前扫描几条)
|
||||
Probability int `json:"probability"` // 激活概率(0-100)
|
||||
UseProbability bool `json:"use_probability"` // 是否使用概率
|
||||
Group string `json:"group"` // 分组(同组只激活一个)
|
||||
GroupOverride bool `json:"group_override"` // 分组覆盖
|
||||
GroupWeight int `json:"group_weight"` // 分组权重
|
||||
PreventRecursion bool `json:"prevent_recursion"` // 防止递归激活
|
||||
DelayUntilRecursion bool `json:"delay_until_recursion"` // 延迟到递归时激活
|
||||
ScanDepth *int `json:"scan_depth"` // 扫描深度(nil=使用全局设置)
|
||||
CaseSensitive *bool `json:"case_sensitive"` // 大小写敏感(nil=使用全局设置)
|
||||
MatchWholeWords *bool `json:"match_whole_words"` // 匹配整个单词
|
||||
UseRegex *bool `json:"use_regex"` // 使用正则表达式
|
||||
Automation string `json:"automation_id"` // 自动化ID
|
||||
Role string `json:"role"` // 角色(system/user/assistant)
|
||||
VectorizedContent string `json:"vectorized"` // 向量化的内容ID
|
||||
Extensions map[string]interface{} `json:"extensions"` // 扩展数据
|
||||
}
|
||||
|
||||
// AICharacterWorldInfo 角色关联的世界书(中间表)
|
||||
type AICharacterWorldInfo struct {
|
||||
global.GVA_MODEL
|
||||
CharacterID uint `json:"characterId" gorm:"not null;index:idx_char_world,unique;comment:角色ID"`
|
||||
Character *AICharacter `json:"character" gorm:"foreignKey:CharacterID"`
|
||||
WorldInfoID uint `json:"worldInfoId" gorm:"not null;index:idx_char_world,unique;comment:世界书ID"`
|
||||
WorldInfo *AIWorldInfo `json:"worldInfo" gorm:"foreignKey:WorldInfoID"`
|
||||
}
|
||||
|
||||
func (AICharacterWorldInfo) TableName() string {
|
||||
return "ai_character_world_info"
|
||||
}
|
||||
|
||||
71
server/model/app/request/world_info.go
Normal file
71
server/model/app/request/world_info.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package request
|
||||
|
||||
import "git.echol.cn/loser/st/server/model/app"
|
||||
|
||||
// CreateWorldBookRequest 创建世界书请求
|
||||
type CreateWorldBookRequest struct {
|
||||
BookName string `json:"bookName" binding:"required,min=1,max=500"`
|
||||
IsGlobal bool `json:"isGlobal"`
|
||||
Entries []app.AIWorldInfoEntry `json:"entries" binding:"required"`
|
||||
LinkedChars []string `json:"linkedChars"`
|
||||
}
|
||||
|
||||
// UpdateWorldBookRequest 更新世界书请求
|
||||
type UpdateWorldBookRequest struct {
|
||||
BookName string `json:"bookName" binding:"required,min=1,max=500"`
|
||||
IsGlobal bool `json:"isGlobal"`
|
||||
Entries []app.AIWorldInfoEntry `json:"entries" binding:"required"`
|
||||
LinkedChars []string `json:"linkedChars"`
|
||||
}
|
||||
|
||||
// WorldBookListRequest 世界书列表查询请求
|
||||
type WorldBookListRequest struct {
|
||||
PageInfo
|
||||
BookName string `json:"bookName" form:"bookName"` // 世界书名称(模糊搜索)
|
||||
IsGlobal *bool `json:"isGlobal" form:"isGlobal"` // 是否全局
|
||||
CharacterID *uint `json:"characterId" form:"characterId"` // 关联角色ID
|
||||
}
|
||||
|
||||
// CreateWorldEntryRequest 创建世界书条目请求
|
||||
type CreateWorldEntryRequest struct {
|
||||
BookID uint `json:"bookId" binding:"required"`
|
||||
Entry app.AIWorldInfoEntry `json:"entry" binding:"required"`
|
||||
}
|
||||
|
||||
// UpdateWorldEntryRequest 更新世界书条目请求
|
||||
type UpdateWorldEntryRequest struct {
|
||||
BookID uint `json:"bookId" binding:"required"`
|
||||
Entry app.AIWorldInfoEntry `json:"entry" binding:"required"`
|
||||
}
|
||||
|
||||
// DeleteWorldEntryRequest 删除世界书条目请求
|
||||
type DeleteWorldEntryRequest struct {
|
||||
BookID uint `json:"bookId" binding:"required"`
|
||||
EntryID string `json:"entryId" binding:"required"`
|
||||
}
|
||||
|
||||
// LinkCharacterRequest 关联角色请求
|
||||
type LinkCharacterRequest struct {
|
||||
BookID uint `json:"bookId" binding:"required"`
|
||||
CharacterIDs []uint `json:"characterIds" binding:"required"`
|
||||
}
|
||||
|
||||
// WorldBookImportRequest 导入世界书请求
|
||||
type WorldBookImportRequest struct {
|
||||
BookName string `json:"bookName" binding:"required"`
|
||||
Format string `json:"format" binding:"required,oneof=json lorebook"`
|
||||
}
|
||||
|
||||
// WorldBookExportRequest 导出世界书请求
|
||||
type WorldBookExportRequest struct {
|
||||
BookID uint `json:"bookId" binding:"required"`
|
||||
Format string `json:"format" binding:"required,oneof=json lorebook"`
|
||||
}
|
||||
|
||||
// MatchWorldInfoRequest 匹配世界书条目请求(用于聊天时激活)
|
||||
type MatchWorldInfoRequest struct {
|
||||
CharacterID uint `json:"characterId" binding:"required"`
|
||||
Messages []string `json:"messages" binding:"required"`
|
||||
ScanDepth int `json:"scanDepth" binding:"min=1,max=100"`
|
||||
MaxTokens int `json:"maxTokens" binding:"min=100"`
|
||||
}
|
||||
76
server/model/app/response/world_info.go
Normal file
76
server/model/app/response/world_info.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"git.echol.cn/loser/st/server/model/app"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WorldBookResponse 世界书响应
|
||||
type WorldBookResponse struct {
|
||||
ID uint `json:"id"`
|
||||
UserID uint `json:"userId"`
|
||||
BookName string `json:"bookName"`
|
||||
IsGlobal bool `json:"isGlobal"`
|
||||
Entries []app.AIWorldInfoEntry `json:"entries"`
|
||||
LinkedChars []string `json:"linkedChars"`
|
||||
EntryCount int `json:"entryCount"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// WorldBookListResponse 世界书列表响应
|
||||
type WorldBookListResponse struct {
|
||||
List []WorldBookResponse `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
|
||||
// WorldBookExportData 世界书导出数据
|
||||
type WorldBookExportData struct {
|
||||
Name string `json:"name"`
|
||||
Entries []app.AIWorldInfoEntry `json:"entries"`
|
||||
}
|
||||
|
||||
// MatchedWorldInfoEntry 匹配到的世界书条目
|
||||
type MatchedWorldInfoEntry struct {
|
||||
Content string `json:"content"`
|
||||
Position string `json:"position"`
|
||||
Order int `json:"order"`
|
||||
Source string `json:"source"` // 来源世界书名称
|
||||
}
|
||||
|
||||
// MatchWorldInfoResponse 匹配世界书响应
|
||||
type MatchWorldInfoResponse struct {
|
||||
Entries []MatchedWorldInfoEntry `json:"entries"`
|
||||
TotalTokens int `json:"totalTokens"`
|
||||
}
|
||||
|
||||
// ToWorldBookResponse 转换为世界书响应
|
||||
func ToWorldBookResponse(book *app.AIWorldInfo) WorldBookResponse {
|
||||
var entries []app.AIWorldInfoEntry
|
||||
if book.Entries != nil {
|
||||
_ = json.Unmarshal([]byte(book.Entries), &entries)
|
||||
}
|
||||
if entries == nil {
|
||||
entries = []app.AIWorldInfoEntry{}
|
||||
}
|
||||
|
||||
linkedChars := book.LinkedChars
|
||||
if linkedChars == nil {
|
||||
linkedChars = []string{}
|
||||
}
|
||||
|
||||
return WorldBookResponse{
|
||||
ID: book.ID,
|
||||
UserID: book.UserID,
|
||||
BookName: book.BookName,
|
||||
IsGlobal: book.IsGlobal,
|
||||
Entries: entries,
|
||||
LinkedChars: linkedChars,
|
||||
EntryCount: len(entries),
|
||||
CreatedAt: book.CreatedAt,
|
||||
UpdatedAt: book.UpdatedAt,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user