Files

243 lines
9.5 KiB
Go
Raw Permalink Normal View History

// Package types defines data structures and types used throughout the system
// These types are shared across different service modules to ensure data consistency
package types
import (
"strings"
"time"
"gorm.io/gorm"
)
// ChunkType 定义了不同类型的 Chunk
type ChunkType = string
const (
// ChunkTypeText 表示普通的文本 Chunk
ChunkTypeText ChunkType = "text"
// ChunkTypeParentText 表示父子分块策略中的父文本 Chunk(仅用于上下文,不参与向量索引)
ChunkTypeParentText ChunkType = "parent_text"
// ChunkTypeImageOCR 表示图片 OCR 文本的 Chunk
ChunkTypeImageOCR ChunkType = "image_ocr"
// ChunkTypeImageCaption 表示图片描述的 Chunk
ChunkTypeImageCaption ChunkType = "image_caption"
// ChunkTypeSummary 表示摘要类型的 Chunk
ChunkTypeSummary = "summary"
// ChunkTypeEntity 表示实体类型的 Chunk
ChunkTypeEntity ChunkType = "entity"
// ChunkTypeRelationship 表示关系类型的 Chunk
ChunkTypeRelationship ChunkType = "relationship"
// ChunkTypeFAQ 表示 FAQ 条目 Chunk
ChunkTypeFAQ ChunkType = "faq"
// ChunkTypeWebSearch 表示 Web 搜索结果的 Chunk
ChunkTypeWebSearch ChunkType = "web_search"
// ChunkTypeTableSummary 表示数据表摘要的 Chunk
ChunkTypeTableSummary ChunkType = "table_summary"
// ChunkTypeTableColumn 表示数据表列描述的 Chunk
ChunkTypeTableColumn ChunkType = "table_column"
// ChunkTypeWikiPage 表示 Wiki 页面同步的 Chunk,用于将 wiki 页面接入现有检索管线
ChunkTypeWikiPage ChunkType = "wiki_page"
)
// ChunkStatus 定义了不同状态的 Chunk
type ChunkStatus int
const (
ChunkStatusDefault ChunkStatus = 0
// ChunkStatusStored 表示已存储的 Chunk
ChunkStatusStored ChunkStatus = 1
// ChunkStatusIndexed 表示已索引的 Chunk
ChunkStatusIndexed ChunkStatus = 2
)
// ChunkFlags 定义 Chunk 的标志位,用于管理多个布尔状态
type ChunkFlags int
const (
// ChunkFlagRecommended 表示可推荐状态(1 << 0 = 1)
// 当设置此标志时,该 Chunk 可以被推荐给用户
ChunkFlagRecommended ChunkFlags = 1 << 0
// 未来可扩展更多标志位:
// ChunkFlagPinned ChunkFlags = 1 << 1 // 置顶
// ChunkFlagHot ChunkFlags = 1 << 2 // 热门
)
// HasFlag 检查是否设置了指定标志
func (f ChunkFlags) HasFlag(flag ChunkFlags) bool {
return f&flag != 0
}
// SetFlag 设置指定标志
func (f ChunkFlags) SetFlag(flag ChunkFlags) ChunkFlags {
return f | flag
}
// ClearFlag 清除指定标志
func (f ChunkFlags) ClearFlag(flag ChunkFlags) ChunkFlags {
return f &^ flag
}
// ToggleFlag 切换指定标志
func (f ChunkFlags) ToggleFlag(flag ChunkFlags) ChunkFlags {
return f ^ flag
}
// ImageInfo 表示与 Chunk 关联的图片信息
type ImageInfo struct {
// 图片URL(COS)
URL string `json:"url" gorm:"type:text"`
// 原始图片URL
OriginalURL string `json:"original_url" gorm:"type:text"`
// 图片在文本中的开始位置
StartPos int `json:"start_pos"`
// 图片在文本中的结束位置
EndPos int `json:"end_pos"`
// 图片描述
Caption string `json:"caption"`
// 图片OCR文本
OCRText string `json:"ocr_text"`
}
// VideoInfo 表示与 Chunk 关联的视频信息
type VideoInfo struct {
// 视频URL
URL string `json:"url" gorm:"type:text"`
}
// Chunk represents a document chunk
// Chunks are meaningful text segments extracted from original documents
// and are the basic units of knowledge base retrieval
// Each chunk contains a portion of the original content
// and maintains its positional relationship with the original text
// Chunks can be independently embedded as vectors and retrieved, supporting precise content localization
type Chunk struct {
// Unique identifier of the chunk, using UUID format
ID string `json:"id" gorm:"type:varchar(36);primaryKey"`
// SeqID is an auto-increment integer ID for external API usage (FAQ entries)
SeqID int64 `json:"seq_id" gorm:"type:bigint;uniqueIndex;autoIncrement"`
// Tenant ID, used for multi-tenant isolation
TenantID uint64 `json:"tenant_id"`
// ID of the parent knowledge, associated with the Knowledge model
KnowledgeID string `json:"knowledge_id"`
// ID of the knowledge base, for quick location
KnowledgeBaseID string `json:"knowledge_base_id"`
// Optional tag ID for categorization within a knowledge base (used for FAQ)
TagID string `json:"tag_id" gorm:"type:varchar(36);index"`
// Actual text content of the chunk
Content string `json:"content"`
// SourceContent is the immutable parser output. Legacy rows are lazily
// backfilled from Content on the first manual edit.
SourceContent string `json:"-"`
// ContentRevision is incremented for every user edit or rollback.
ContentRevision int `json:"content_revision" gorm:"not null;default:0"`
// IndexStatus reports whether the current content is reflected in the
// retrieval stores: ready | processing | failed.
IndexStatus string `json:"index_status" gorm:"type:varchar(16);not null;default:'ready'"`
// LastEditorID records the actor that produced the current revision.
LastEditorID string `json:"last_editor_id" gorm:"type:varchar(64);not null;default:''"`
// Index position of the chunk in the original document
ChunkIndex int `json:"chunk_index"`
// Whether the chunk is enabled, can be used to temporarily disable certain chunks
IsEnabled bool `json:"is_enabled" gorm:"default:true"`
// Flags 存储多个布尔状态的位标志(如推荐状态等)
// 默认值为 ChunkFlagRecommended (1),表示默认可推荐
Flags ChunkFlags `json:"flags" gorm:"default:1"`
// Status of the chunk
Status int `json:"status" gorm:"default:0"`
// Starting character position in the original text
StartAt int `json:"start_at"`
// Ending character position in the original text
EndAt int `json:"end_at"`
// Previous chunk ID
PreChunkID string `json:"pre_chunk_id"`
// Next chunk ID
NextChunkID string `json:"next_chunk_id"`
// Chunk 类型,用于区分不同类型的 Chunk
ChunkType ChunkType `json:"chunk_type" gorm:"type:varchar(20);default:'text'"`
// 父 Chunk ID,用于关联图片 Chunk 和原始文本 Chunk
ParentChunkID string `json:"parent_chunk_id" gorm:"type:varchar(36);index"`
// 关系 Chunk ID,用于关联关系 Chunk 和原始文本 Chunk
RelationChunks JSON `json:"relation_chunks" gorm:"type:json"`
// 间接关系 Chunk ID,用于关联间接关系 Chunk 和原始文本 Chunk
IndirectRelationChunks JSON `json:"indirect_relation_chunks" gorm:"type:json"`
// Metadata 存储 chunk 级别的扩展信息,例如 FAQ 元数据
Metadata JSON `json:"metadata" gorm:"type:json"`
// ContentHash 存储内容的 hash 值,用于快速匹配(主要用于 FAQ)
ContentHash string `json:"content_hash" gorm:"type:varchar(64);index"`
// 图片信息,存储为 JSON
ImageInfo string `json:"image_info" gorm:"type:text"`
// Chunk creation time
CreatedAt time.Time `json:"created_at"`
// Chunk last update time
UpdatedAt time.Time `json:"updated_at"`
// Soft delete marker, supports data recovery
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
// ContextHeader is a Markdown heading breadcrumb prepended when indexing.
// It is persisted so a later content edit can rebuild the same index input.
ContextHeader string `json:"-" gorm:"type:text"`
}
// ChunkRevision is an immutable snapshot of a superseded chunk revision.
// The current content lives on Chunk; this table stores prior versions.
type ChunkRevision struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey"`
TenantID uint64 `json:"tenant_id" gorm:"index"`
KnowledgeBaseID string `json:"knowledge_base_id" gorm:"type:varchar(36);index"`
KnowledgeID string `json:"knowledge_id" gorm:"type:varchar(36);index"`
ChunkID string `json:"chunk_id" gorm:"type:varchar(36);uniqueIndex:idx_chunk_revision"`
Revision int `json:"revision" gorm:"uniqueIndex:idx_chunk_revision"`
Content string `json:"content" gorm:"type:text"`
IsEnabled bool `json:"is_enabled"`
EditorID string `json:"editor_id" gorm:"type:varchar(64)"`
EditSource string `json:"edit_source" gorm:"type:varchar(16)"`
EditedAt time.Time `json:"edited_at"`
CreatedAt time.Time `json:"created_at"`
}
// EmbeddingContent returns the chunk content with ContextHeader prepended
// when set. Use this where the embedding model needs section context that
// isn't part of the literal Content. Surrounding whitespace on Content is
// trimmed so leading/trailing newlines from boundary slicing don't dilute
// the embedded vector.
func (c *Chunk) EmbeddingContent() string {
if c == nil {
return ""
}
body := strings.TrimSpace(c.Content)
if c.ContextHeader == "" {
return body
}
return c.ContextHeader + "\n\n" + body
}
// AssignChunkSeqIDs assigns sequential SeqIDs to a batch of chunks that have SeqID == 0.
// Must be called before CreateInBatches for SQLite compatibility.
func AssignChunkSeqIDs(tx *gorm.DB, chunks []*Chunk) error {
needAssign := false
for _, c := range chunks {
if c.SeqID == 0 {
needAssign = true
break
}
}
if !needAssign {
return nil
}
var maxSeqID *int64
if err := tx.Unscoped().Model(&Chunk{}).Select("MAX(seq_id)").Scan(&maxSeqID).Error; err != nil {
return err
}
next := int64(1)
if maxSeqID != nil {
next = *maxSeqID + 1
}
for _, c := range chunks {
if c.SeqID == 0 {
c.SeqID = next
next++
}
}
return nil
}