Files
xinmi/internal/types/chunk.go
T
yawan f7fc992f57
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
2026-09-22 16:19:06 +08:00

243 lines
9.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
}