Documentation
¶
Overview ¶
Package interfaces defines the interface contracts for custom agent management
Package interfaces defines the interface contracts between different system components Through interface definitions, business logic can be decoupled from specific implementations, improving code testability and maintainability Knowledge base related interfaces are used to manage knowledge base resources and their contents
Index ¶
- Variables
- type AgentEngine
- type AgentService
- type AgentShareRepository
- type AgentShareService
- type AgentStreamEvent
- type AttachmentDocument
- type AttachmentImage
- type AttachmentImageRead
- type AttachmentReader
- type AuditLogQuery
- type AuditLogRepository
- type AuditLogService
- type AuthTokenRepository
- type ChunkImageInfo
- type ChunkRepository
- type ChunkService
- type CustomAgentRepository
- type CustomAgentService
- type DataSourceRepository
- type DataSourceService
- type DatasetService
- type DocReader
- type DocumentReader
- type EmbedChannelRepository
- type EmbedChannelService
- type EngineFactory
- type EvalHook
- type EvaluationService
- type FileService
- type FilteredWebSearchProvider
- type KBResourceLookup
- type KBShareRepository
- type KBShareService
- type KnowledgeBaseProfileService
- type KnowledgeBaseRepository
- type KnowledgeBaseService
- type KnowledgeBaseTaskCanceller
- type KnowledgeIndexMover
- type KnowledgeRepository
- type KnowledgeService
- type KnowledgeTagRepository
- type KnowledgeTagService
- type MCPEndpointRepository
- type MCPEndpointService
- type MCPEndpointUpdate
- type MCPMetadataRepository
- type MCPMetadataService
- type MCPOAuthRepository
- type MCPServiceRepository
- type MCPServiceService
- type MCPToolApprovalRepository
- type MCPToolApprovalService
- type MemoryExtractionFailure
- type MemoryRecall
- type MemoryRepository
- type MemoryScope
- type MemorySearchResult
- type MemoryService
- type MemoryVectorHit
- type MemoryVectorQuery
- type MessageFileBindingLookup
- type MessageRepository
- type MessageService
- type MessageSuggestionRepository
- type MessageSuggestionService
- type Metrics
- type ModelRepository
- type ModelService
- type OrganizationRepository
- type OrganizationService
- type ResourceCatalog
- type ResourceCleaner
- type ResourceRegistration
- type ResourceRepository
- type RetrievalContext
- type RetrieveEngine
- type RetrieveEngineRegistry
- type RetrieveEngineRepository
- type RetrieveEngineService
- type RetrieveGraphRepository
- type RuntimeTaskInspector
- type SessionRepository
- type SessionService
- type StorageBackendRepository
- type StorageBackendResolver
- type StorageBackendService
- type StoreRegistry
- type StreamEvent
- type StreamManager
- type SyncLogRepository
- type SystemSettingRepository
- type SystemSettingService
- type TaskDeadLetterRepository
- type TaskEnqueuer
- type TaskHandler
- type TaskInspector
- type TaskPendingOpsClaimableCounter
- type TaskPendingOpsDrainer
- type TaskPendingOpsFinalizingSeeder
- type TaskPendingOpsKnowledgeBaseGuard
- type TaskPendingOpsRepository
- type TaskPendingOpsScopeCleaner
- type TaskPendingOpsTenantLiveness
- type TemporaryDocumentRepository
- type TemporaryDocumentService
- type TenantAPIKeyCreateRequest
- type TenantAPIKeyCreateResult
- type TenantAPIKeyRepository
- type TenantAPIKeyService
- type TenantAPIKeyUpdateRequest
- type TenantDisabledSharedAgentRepository
- type TenantInvitationRepository
- type TenantInvitationService
- type TenantMemberRepository
- type TenantMemberService
- type TenantRepository
- type TenantService
- type UserRepository
- type UserResourceFavoriteRepository
- type UserResourceFavoriteService
- type UserService
- type VectorStoreRepository
- type VectorStoreService
- type WeKnoraCloudService
- type WebSearchProvider
- type WebSearchProviderRepository
- type WebSearchProviderService
- type WebSearchService
- type WebSearchStateService
- type WikiPageRepository
- type WikiPageService
Constants ¶
This section is empty.
Variables ¶
var ErrAttachmentNotFound = errors.New("attachment is not in this conversation")
ErrAttachmentNotFound indicates that a reference is absent from the authorized inventory.
Functions ¶
This section is empty.
Types ¶
type AgentEngine ¶
type AgentEngine interface {
// Execute executes the agent with conversation history and returns a stream of events
// imageURLs is optional - when provided, images are passed to the LLM as multimodal content
Execute(
ctx context.Context,
sessionID, messageID, query string,
llmContext []chat.Message,
imageURLs ...[]string,
) (*types.AgentState, error)
// SetMemoryPrompt supplies the long-term memory envelope for this run.
// It must be called before Execute; an empty string is a no-op.
SetMemoryPrompt(prompt string)
// SetSteerSink enables mid-run message injection for this run. Nil
// (default) disables it; when set, the engine drains user-appended
// messages at every round boundary and persists accepted ones through
// the sink. Must be called before Execute.
SetSteerSink(sink types.SteerSink)
// SetContextCheckpointSink enables persisting compaction summaries that
// end on a stored turn, so later turns start from them. Nil (default)
// keeps compaction local to the turn. Must be called before Execute.
SetContextCheckpointSink(sink types.ContextCheckpointSink)
}
AgentEngine defines the interface for agent execution engine
type AgentService ¶
type AgentService interface {
// CreateAgentEngine creates an agent engine with the given configuration and EventBus.
// Conversation history is loaded by the caller (see service.LoadAgentHistory) and
// passed into AgentEngine.Execute; the engine itself is stateless across turns.
CreateAgentEngine(
ctx context.Context,
config *types.AgentConfig,
chatModel chat.Chat,
rerankModel rerank.Reranker,
eventBus *event.EventBus,
sessionID, assistantMessageID string,
) (AgentEngine, error)
// ValidateConfig validates an agent configuration
ValidateConfig(config *types.AgentConfig) error
}
AgentService defines the interface for agent-related operations
type AgentShareRepository ¶
type AgentShareRepository interface {
GetShareByAgentIDForTenant(ctx context.Context, tenantID uint64, agentID string, excludeTenantID uint64) (*types.AgentShare, error)
}
AgentShareRepository defines the agent sharing repository interface
type AgentShareService ¶
type AgentShareService interface {
ListSharedAgentsInOrganizations(ctx context.Context, orgIDs []string, tenantID uint64, callerTenantRole types.TenantRole) (map[string][]*types.OrganizationSharedAgentItem, error)
SetSharedAgentDisabledByMe(ctx context.Context, tenantID uint64, agentID string, sourceTenantID uint64, disabled bool) error
GetSharedAgentForTenant(ctx context.Context, tenantID uint64, callerTenantRole types.TenantRole, agentID string, sourceTenantID ...uint64) (*types.CustomAgent, error)
// TenantCanAccessKBViaSomeSharedAgent returns true if the caller's tenant has at least one shared agent that can access the given KB (for opening KB detail from "通过智能体可见" list without passing agent_id).
GetShareByAgentIDForTenant(ctx context.Context, tenantID uint64, agentID string, excludeTenantID uint64) (*types.AgentShare, error)
CountByOrganizations(ctx context.Context, orgIDs []string) (map[string]int64, error)
}
AgentShareService defines the agent sharing service interface.
Plan 3 of #1303: visibility and access checks key on the caller's tenant. callerTenantRole flows through so the 3-D cap (tenant Viewer → at most OrgRoleViewer) can be applied consistently.
type AgentStreamEvent ¶
type AgentStreamEvent struct {
Type string `json:"type"` // "thought", "tool_call", "tool_result", "final_answer", "error", "references"
Content string `json:"content"` // Incremental content
Data map[string]interface{} `json:"data"` // Additional structured data
Done bool `json:"done"` // Whether this is the last event
Iteration int `json:"iteration"` // Current iteration number
}
AgentStreamEvent represents a streaming event from the agent
type AttachmentDocument ¶
type AttachmentDocument struct {
Attachment types.MessageAttachment
Document *types.TemporaryDocument
ParsedPath string
Warning string
}
AttachmentDocument contains a durable result and any successfully materialized text path.
type AttachmentImage ¶
type AttachmentImage struct {
DataURI string
// Non-nil means description was attempted; an empty value must not trigger
// another VLM call in the engine. Native vision keeps this nil.
Description *string
Files map[string]string
Warning string
}
AttachmentImage is prepared visual evidence with an optional scoped observation.
type AttachmentImageRead ¶
type AttachmentImageRead struct {
Number int
Image *AttachmentImage
Err error
}
AttachmentImageRead is the outcome for one requested image number.
type AttachmentReader ¶
type AttachmentReader interface {
List(context.Context) ([]AttachmentDocument, error)
Resolve(ctx context.Context, ref string, wait bool) (*AttachmentDocument, error)
// ReadImages authorizes the document once and reads the numbered images
// concurrently. The error covers the whole batch; per-image failures are
// reported in the aligned results.
ReadImages(
ctx context.Context, documentID string, numbers []int, detail, focus string,
) ([]AttachmentImageRead, error)
WaitTimeout() time.Duration
}
AttachmentReader is the application boundary used by attachment:// tools. Implementations authorize against the session inventory before opening content.
type AuditLogQuery ¶
type AuditLogQuery struct {
AfterID uint64
Limit int
Action types.AuditAction
Outcome types.AuditOutcome
ActorUserID string
ScopeType string
ScopeID string
UnscopedOnly bool
}
AuditLogQuery is the cursor + filter set for listing audit log entries. AfterID is the last id from the previous page (rows with id < AfterID are returned, newest first); 0 means "from the top". Limit is capped at 100 inside the repository regardless of caller input — keeps unbounded scans off the table.
type AuditLogRepository ¶
type AuditLogRepository interface {
Create(ctx context.Context, entry *types.AuditLog) error
List(ctx context.Context, tenantID uint64, q *AuditLogQuery) ([]*types.AuditLog, error)
// CountSinceForDedup is the rate-limit primitive for LogDenied —
// returns the count of matching rows in the trailing window so the
// service can skip writing duplicates. Filter is
// (tenant_id, actor_user_id, action, request_path, created_at >= since).
CountSinceForDedup(
ctx context.Context,
tenantID uint64,
actorUserID string,
action types.AuditAction,
requestPath string,
since time.Time,
) (int64, error)
// DeleteOlderThan removes audit rows whose created_at is strictly
// before cutoff and returns the affected row count. It is the
// retention primitive driven by the daily background sweep.
// Implementations should delete in a single statement (no per-row
// fetch) so the long-tail cost stays at "one DELETE per sweep".
DeleteOlderThan(ctx context.Context, cutoff time.Time) (int64, error)
}
AuditLogRepository is the storage primitive for the audit table. All writes are inserts (immutable rows); the only "update" surface is none — once written, an entry is permanent.
type AuditLogService ¶
type AuditLogService interface {
// Log writes a single audit entry. Callers fill TenantID + Action +
// any per-event fields; the service fills CreatedAt if zero.
Log(ctx context.Context, entry *types.AuditLog) error
// LogDenied records a middleware-level reject decision. Subject to
// 1-minute sliding-window dedup keyed by
// (tenant_id, actor_user_id, action, request_path) so a probing
// client cannot flood the table.
LogDenied(
ctx context.Context,
c *gin.Context,
tenantID uint64,
actorUserID, actorRole string,
requiredRole types.TenantRole,
) error
List(ctx context.Context, tenantID uint64, q *AuditLogQuery) ([]*types.AuditLog, error)
// Purge deletes rows whose created_at is strictly older than the
// retention horizon. retentionDays <= 0 makes the call a no-op,
// which keeps the daily sweep cheap when retention is disabled.
// Returns rows deleted; transient repo errors propagate.
Purge(ctx context.Context, retentionDays int) (int64, error)
}
AuditLogService is the high-level audit API the rest of the codebase uses. It owns timestamp defaulting (Log) and rate-limit dedup (LogDenied) so callers don't have to think about either.
type AuthTokenRepository ¶
type AuthTokenRepository interface {
// CreateToken creates an auth token
CreateToken(ctx context.Context, token *types.AuthToken) error
// GetTokenByValue gets a token by its value
GetTokenByValue(ctx context.Context, tokenValue string) (*types.AuthToken, error)
// GetTokenByID gets a token by its primary key
GetTokenByID(ctx context.Context, id string) (*types.AuthToken, error)
// GetTokensByUserID gets all tokens for a user
GetTokensByUserID(ctx context.Context, userID string) ([]*types.AuthToken, error)
// UpdateToken updates a token
UpdateToken(ctx context.Context, token *types.AuthToken) error
// DeleteToken deletes a token
DeleteToken(ctx context.Context, id string) error
// DeleteExpiredTokens deletes all expired tokens
DeleteExpiredTokens(ctx context.Context) error
// RevokeTokensByUserID revokes all tokens for a user
RevokeTokensByUserID(ctx context.Context, userID string) error
}
AuthTokenRepository defines the auth token repository interface
type ChunkImageInfo ¶
type ChunkImageInfo struct {
KnowledgeID string `gorm:"column:knowledge_id"`
ImageInfo string `gorm:"column:image_info"`
}
ChunkImageInfo holds (knowledge_id, image_info) pairs for image cleanup before chunk deletion.
type ChunkRepository ¶
type ChunkRepository interface {
// ListAllChunksByKnowledgeID includes every type and storage status for
// scoped lifecycle operations; UI pagination hides unindexed chunks.
ListAllChunksByKnowledgeID(ctx context.Context, tenantID uint64, knowledgeID string) ([]*types.Chunk, error)
// CreateChunks creates chunks
CreateChunks(ctx context.Context, chunks []*types.Chunk) error
// GetChunkByID gets a chunk by id
GetChunkByID(ctx context.Context, tenantID uint64, id string) (*types.Chunk, error)
// GetChunkByIDOnly gets a chunk by id without tenant filter (for permission resolution)
GetChunkByIDOnly(ctx context.Context, id string) (*types.Chunk, error)
// GetChunkBySeqID gets a chunk by seq_id
GetChunkBySeqID(ctx context.Context, tenantID uint64, seqID int64) (*types.Chunk, error)
// ListChunksByID lists chunks by ids
ListChunksByID(ctx context.Context, tenantID uint64, ids []string) ([]*types.Chunk, error)
// ListChunksByIDOnly lists chunks by ids without tenant filter (for shared KB resolution).
ListChunksByIDOnly(ctx context.Context, ids []string) ([]*types.Chunk, error)
// ListChunksBySeqID lists chunks by seq_ids
ListChunksBySeqID(ctx context.Context, tenantID uint64, seqIDs []int64) ([]*types.Chunk, error)
// ListChunksByKnowledgeID lists the knowledge's text chunks. Despite the name
// it filters to chunk_type = 'text'; reach for ListChunksByKnowledgeIDAndTypes
// when summary, parent_text or image chunks are needed too.
ListChunksByKnowledgeID(ctx context.Context, tenantID uint64, knowledgeID string) ([]*types.Chunk, error)
// ListChunksByKnowledgeIDAndTypes lists the knowledge's chunks restricted to
// the given chunk types, ordered by chunk_index.
ListChunksByKnowledgeIDAndTypes(
ctx context.Context, tenantID uint64, knowledgeID string, chunkTypes []types.ChunkType,
) ([]*types.Chunk, error)
// ListPagedChunksByKnowledgeID lists paged chunks by knowledge id.
// When tagIDs is non-empty, results are filtered by tag_id (OR semantics).
// knowledgeType: "faq" or "manual" - determines sort order and search behavior
// - FAQ: sorts by updated_at, searchField can be "standard_question", "similar_questions", "answers", or "" for all
// - Document (manual): sorts by chunk_index, keyword searches content only
// sortOrder: "asc" for ascending, default is descending
// searchField: specifies which field to search in (only applicable for FAQ type)
// isEnabled: when non-nil, filters chunks by their enabled state. Agent/model
// consumers must pass true so disabled content never enters model context.
ListPagedChunksByKnowledgeID(
ctx context.Context,
tenantID uint64,
knowledgeID string,
page *types.Pagination,
chunkType []types.ChunkType,
tagIDs []string,
keyword string,
searchField string,
sortOrder string,
knowledgeType string,
isEnabled *bool,
) ([]*types.Chunk, int64, error)
ListChunkByParentID(ctx context.Context, tenantID uint64, parentID string) ([]*types.Chunk, error)
// ListChunkNeighbors returns up to `before` enabled chunks immediately
// preceding chunkIndex and up to `after` immediately following it, in
// document order, restricted to chunkTypes. It walks chunk_index rather
// than list positions, so gaps left by other chunk types (parents,
// summaries, images) do not shift the neighbourhood.
ListChunkNeighbors(
ctx context.Context,
tenantID uint64,
knowledgeID string,
chunkIndex int,
before int,
after int,
chunkTypes []types.ChunkType,
) ([]*types.Chunk, error)
// ListChunksByParentIDs lists chunks whose parent_chunk_id is in the given list
ListChunksByParentIDs(ctx context.Context, tenantID uint64, parentIDs []string) ([]*types.Chunk, error)
// ListChunksByParentIDsOnly lists chunks by parent IDs without tenant filter
// (for shared KB resolution).
ListChunksByParentIDsOnly(ctx context.Context, parentIDs []string) ([]*types.Chunk, error)
// UpdateChunk updates a chunk
UpdateChunk(ctx context.Context, chunk *types.Chunk) error
// CreateChunkRevision stores an immutable snapshot of a superseded revision.
CreateChunkRevision(ctx context.Context, revision *types.ChunkRevision) error
// SaveChunkRevision atomically snapshots the old row and applies the new
// row only when its revision still matches expectedRevision.
SaveChunkRevision(ctx context.Context, chunk *types.Chunk, revision *types.ChunkRevision, expectedRevision int) error
// ListChunkRevisions returns snapshots ordered newest first.
ListChunkRevisions(ctx context.Context, tenantID uint64, chunkID string) ([]*types.ChunkRevision, error)
// GetChunkRevision returns one historical snapshot.
GetChunkRevision(ctx context.Context, tenantID uint64, chunkID string, revision int) (*types.ChunkRevision, error)
// UpdateChunks updates chunks in batch
UpdateChunks(ctx context.Context, chunks []*types.Chunk) error
// SaveChunks persists full chunk objects in a single transaction using GORM Save (UPDATE).
SaveChunks(ctx context.Context, chunks []*types.Chunk) error
// UpdateChunkFieldsByIDs sets the same column values (e.g. {"status": 2})
// on every listed chunk of the tenant with one UPDATE per batch of IDs.
// updated_at is set automatically.
UpdateChunkFieldsByIDs(ctx context.Context, tenantID uint64, ids []string, fields map[string]interface{}) error
// DeleteChunk deletes a chunk
DeleteChunk(ctx context.Context, tenantID uint64, id string) error
// DeleteChunks deletes chunks by IDs in batch
DeleteChunks(ctx context.Context, tenantID uint64, ids []string) error
// DeleteChunksByKnowledgeID deletes chunks by knowledge id
DeleteChunksByKnowledgeID(ctx context.Context, tenantID uint64, knowledgeID string) error
// DeleteByKnowledgeList deletes all chunks for a knowledge list
DeleteByKnowledgeList(ctx context.Context, tenantID uint64, knowledgeIDs []string) error
// ListImageInfoByKnowledgeIDs returns non-empty (knowledge_id, image_info) pairs for image cleanup.
ListImageInfoByKnowledgeIDs(ctx context.Context, tenantID uint64, knowledgeIDs []string) ([]ChunkImageInfo, error)
// ListImageAssets returns one page of a KB's de-duplicated image assets
// (one per image_info entry, keyed by URL) with the query's filters and
// sort applied in the database, plus the filtered total.
ListImageAssets(
ctx context.Context, tenantID uint64, kbID string, q *types.ImageAssetQuery,
) ([]types.ImageAssetRow, int64, error)
// MoveChunksByKnowledgeID updates knowledge_base_id for all chunks of a knowledge item
MoveChunksByKnowledgeID(ctx context.Context, tenantID uint64, knowledgeID string, targetKBID string) error
// DeleteChunksByTagID deletes all chunks with the specified tag ID
// Returns the IDs of deleted chunks for index cleanup
DeleteChunksByTagID(ctx context.Context, tenantID uint64, kbID string, tagID string, excludeIDs []string) ([]string, error)
// CountChunksByKnowledgeBaseID counts the number of chunks in a knowledge base.
CountChunksByKnowledgeBaseID(ctx context.Context, tenantID uint64, kbID string) (int64, error)
// DeleteUnindexedChunks deletes unindexed chunks by knowledge id and chunk index range
DeleteUnindexedChunks(ctx context.Context, tenantID uint64, knowledgeID string) ([]*types.Chunk, error)
// ListAllFAQChunksByKnowledgeID lists all FAQ chunks for a knowledge ID
// only ID and ContentHash fields for efficiency
ListAllFAQChunksByKnowledgeID(ctx context.Context, tenantID uint64, knowledgeID string) ([]*types.Chunk, error)
// ListAllFAQChunksWithMetadataByKnowledgeBaseID lists all FAQ chunks for a knowledge base ID
// returns ID and Metadata fields for duplicate question checking
ListAllFAQChunksWithMetadataByKnowledgeBaseID(ctx context.Context, tenantID uint64, kbID string) ([]*types.Chunk, error)
// FindFAQChunkWithDuplicateQuestion finds a single FAQ chunk whose standard_question or
// similar_questions overlap with the given question list. Returns nil if no duplicate found.
FindFAQChunkWithDuplicateQuestion(ctx context.Context, tenantID uint64, kbID string, excludeChunkID string, questions []string) (*types.Chunk, error)
// ListAllFAQChunksForExport lists all FAQ chunks for export with full metadata, tag_id, is_enabled, and flags
ListAllFAQChunksForExport(ctx context.Context, tenantID uint64, knowledgeID string) ([]*types.Chunk, error)
// UpdateChunkFlagsBatch updates flags for multiple chunks in batch using a single SQL statement.
// setFlags: map of chunk ID to flags to set (OR operation)
// clearFlags: map of chunk ID to flags to clear (AND NOT operation)
UpdateChunkFlagsBatch(ctx context.Context, tenantID uint64, kbID string, setFlags map[string]types.ChunkFlags, clearFlags map[string]types.ChunkFlags) error
// UpdateChunkFieldsByTagID updates fields for all chunks with the specified tag ID.
// Supports updating is_enabled, flags, and tag_id fields.
// newTagID: if not nil, updates tag_id to this value (empty string means uncategorized)
UpdateChunkFieldsByTagID(ctx context.Context, tenantID uint64, kbID string, tagID string, isEnabled *bool, setFlags types.ChunkFlags, clearFlags types.ChunkFlags, newTagID *string, excludeIDs []string) ([]string, error)
// FAQChunkDiff compares FAQ chunks between two knowledge bases and returns the differences.
FAQChunkDiff(ctx context.Context, srcTenantID uint64, srcKBID string, dstTenantID uint64, dstKBID string) (*types.FAQChunkDiffResult, error)
// ListFAQChunkStatusByIDs loads status fields for FAQ clone sync.
ListFAQChunkStatusByIDs(ctx context.Context, tenantID uint64, ids []string) (map[string]*types.FAQChunkStatus, error)
// ListRecommendedFAQChunks lists FAQ chunks with the recommended flag set.
// Filter by explicitly selected kbIDs, knowledgeIDs, and/or FAQ tagIDs.
// Returns up to `limit` chunks sorted by updated_at descending.
ListRecommendedFAQChunks(ctx context.Context, tenantID uint64, kbIDs []string, knowledgeIDs []string, tagIDs []string, limit int) ([]*types.Chunk, error)
// ListRecentDocumentChunksWithQuestions lists recent document chunks that have generated questions.
// Filter by kbIDs and/or knowledgeIDs. At least one of them must be non-empty.
// Returns up to `limit` chunks sorted by updated_at descending.
ListRecentDocumentChunksWithQuestions(ctx context.Context, tenantID uint64, kbIDs []string, knowledgeIDs []string, limit int) ([]*types.Chunk, error)
}
ChunkRepository defines the interface for chunk repository operations
type ChunkService ¶
type ChunkService interface {
// CreateChunks creates chunks
CreateChunks(ctx context.Context, chunks []*types.Chunk) error
// GetChunkByID gets a chunk by id (uses tenant from context)
GetChunkByID(ctx context.Context, id string) (*types.Chunk, error)
// GetChunkByIDOnly gets a chunk by id without tenant filter (for permission resolution)
GetChunkByIDOnly(ctx context.Context, id string) (*types.Chunk, error)
// ListChunksByKnowledgeID lists chunks by knowledge id
ListChunksByKnowledgeID(ctx context.Context, knowledgeID string) ([]*types.Chunk, error)
// ListPagedChunksByKnowledgeID lists paged chunks by knowledge id
ListPagedChunksByKnowledgeID(
ctx context.Context,
knowledgeID string,
page *types.Pagination,
chunkType []types.ChunkType,
) (*types.PageResult, error)
// ListImagesByKnowledgeBaseID lists the image assets of a KB with keyword
// search, schema-driven attribute filters, sorting and pagination.
ListImagesByKnowledgeBaseID(
ctx context.Context,
kbID string,
page *types.Pagination,
filter *types.ImageListFilter,
) (*types.PageResult, error)
// UpdateChunk updates a chunk
UpdateChunk(ctx context.Context, chunk *types.Chunk) error
// UpdateChunks updates chunks in batch
UpdateChunks(ctx context.Context, chunks []*types.Chunk) error
// DeleteChunk deletes a chunk
DeleteChunk(ctx context.Context, id string) error
// DeleteChunks deletes chunks by IDs in batch
DeleteChunks(ctx context.Context, ids []string) error
// DeleteChunksByKnowledgeID deletes chunks by knowledge id
DeleteChunksByKnowledgeID(ctx context.Context, knowledgeID string) error
// DeleteByKnowledgeList deletes all chunks for a knowledge list
DeleteByKnowledgeList(ctx context.Context, ids []string) error
// ListChunkByParentID lists chunks by parent id
ListChunkByParentID(ctx context.Context, tenantID uint64, parentID string) ([]*types.Chunk, error)
// GetRepository gets the chunk repository
GetRepository() ChunkRepository
// DeleteGeneratedQuestion deletes a single generated question from a chunk by question ID
// This updates the chunk metadata and removes the corresponding vector index
DeleteGeneratedQuestion(ctx context.Context, chunkID string, questionID string) error
// UpdateDocumentChunk applies a revision-checked edit and synchronizes retrieval indices.
UpdateDocumentChunk(ctx context.Context, chunkID string, content *string, isEnabled *bool, expectedRevision *int) (*types.Chunk, error)
// ListChunkRevisions lists immutable snapshots for a chunk.
ListChunkRevisions(ctx context.Context, chunkID string) ([]*types.ChunkRevision, error)
// RevertDocumentChunk restores a historical revision as a new current revision.
RevertDocumentChunk(ctx context.Context, chunkID string, revision int, expectedRevision *int) (*types.Chunk, error)
// UpsertGeneratedQuestion creates or updates a generated retrieval question.
UpsertGeneratedQuestion(ctx context.Context, chunkID string, questionID string, question string) (*types.GeneratedQuestion, error)
}
ChunkService mutations require explicit KB write grants and validate persisted document bindings. Trusted processing/rollback code uses ChunkRepository after its own task admission; the execution tenant alone is not a write grant. ChunkService defines the interface for chunk service operations
type CustomAgentRepository ¶
type CustomAgentRepository interface {
// CreateAgent creates an agent record
// Parameters:
// - ctx: Context information
// - agent: Agent object
// Returns:
// - Possible errors such as database connection failure, unique constraint conflicts, etc.
CreateAgent(ctx context.Context, agent *types.CustomAgent) error
// GetAgentByID queries an agent by ID and tenant
// Parameters:
// - ctx: Context information
// - id: Agent ID
// - tenantID: Tenant ID for isolation
// Returns:
// - Agent object, if found
// - Possible errors such as record not existing, database errors, etc.
GetAgentByID(ctx context.Context, id string, tenantID uint64) (*types.CustomAgent, error)
// ListAgentsByTenantID lists all agents for a specific tenant
// Parameters:
// - ctx: Context information
// - tenantID: Tenant ID
// Returns:
// - List of agent objects
// - Possible errors such as database errors, etc.
ListAgentsByTenantID(ctx context.Context, tenantID uint64) ([]*types.CustomAgent, error)
// UpdateAgent updates an agent record
// Parameters:
// - ctx: Context information
// - agent: Agent object containing update information
// Returns:
// - Possible errors such as record not existing, database errors, etc.
UpdateAgent(ctx context.Context, agent *types.CustomAgent) error
// DeleteAgent deletes an agent record
// Parameters:
// - ctx: Context information
// - id: Agent ID
// - tenantID: Tenant ID for isolation (required for composite primary key)
// Returns:
// - Possible errors such as record not existing, database errors, etc.
DeleteAgent(ctx context.Context, id string, tenantID uint64) error
// CountByModelID counts active agents in the tenant whose config references
// the given model ID (chat, rerank, VLM, ASR, query-understand, etc.).
CountByModelID(ctx context.Context, tenantID uint64, modelID string) (int64, error)
// ListModelUsages returns the minimal active agent projections that
// reference the model, with every matching binding merged per object.
// Implementations must cap the result at types.ModelUsageListLimit;
// callers that need the untruncated size should use CountByModelID.
ListModelUsages(ctx context.Context, tenantID uint64, modelID string) ([]types.ModelUsageResource, error)
// CountBySandboxConfigID counts agents pointing at a sandbox config.
//
// Used only to warn the admin which agents reference a config; never use it
// to refuse operations. Agent references are permanent state, so blocking on
// them would make credential rotation impossible.
CountBySandboxConfigID(ctx context.Context, tenantID uint64, configID string) (int64, error)
// ListNamesBySandboxConfigID returns agent names pointing at a sandbox config.
//
// Used only to warn the admin which agents reference a config; never use it
// to refuse operations. Agent references are permanent state, so blocking on
// them would make credential rotation impossible.
ListNamesBySandboxConfigID(ctx context.Context, tenantID uint64, configID string) ([]string, error)
}
CustomAgentRepository defines the custom agent repository interface Responsible for agent data persistence and retrieval
type CustomAgentService ¶
type CustomAgentService interface {
// CreateAgent creates a new custom agent
// Parameters:
// - ctx: Context information, carrying request tracking, user identity, etc.
// - agent: Agent object containing basic information and configuration
// Returns:
// - Created agent object (including automatically generated ID)
// - Possible errors such as insufficient permissions, validation errors, etc.
CreateAgent(ctx context.Context, agent *types.CustomAgent) (*types.CustomAgent, error)
// GetAgentByID retrieves agent information by ID (uses tenant from context)
// Parameters:
// - ctx: Context information
// - id: Unique identifier of the agent
// Returns:
// - Agent object, if found (including built-in agents)
// - Possible errors such as not existing, insufficient permissions, etc.
GetAgentByID(ctx context.Context, id string) (*types.CustomAgent, error)
// GetAgentByIDAndTenant retrieves agent by ID and tenant (for shared agents; skips built-in resolution)
GetAgentByIDAndTenant(ctx context.Context, id string, tenantID uint64) (*types.CustomAgent, error)
// ListAgents lists all agents under the current tenant (including built-in agents)
// Parameters:
// - ctx: Context information, containing tenant information
// Returns:
// - List of agent objects (built-in agents first, then custom agents sorted by creation time)
// - Possible errors such as insufficient permissions, etc.
ListAgents(ctx context.Context) ([]*types.CustomAgent, error)
// UpdateAgent updates agent information
// Parameters:
// - ctx: Context information
// - agent: Agent object containing update information
// Returns:
// - Updated agent object
// - Possible errors such as not existing, insufficient permissions, cannot modify built-in, etc.
// avatar carries field presence the agent struct cannot express: nil means
// the caller did not send an avatar and the stored one must survive, while
// a pointer to "" is an explicit clear.
UpdateAgent(ctx context.Context, agent *types.CustomAgent, avatar *string) (*types.CustomAgent, error)
// DeleteAgent deletes an agent
// Parameters:
// - ctx: Context information
// - id: Unique identifier of the agent
// Returns:
// - Possible errors such as not existing, insufficient permissions, cannot delete built-in, etc.
DeleteAgent(ctx context.Context, id string) error
// CopyAgent creates a copy of an existing agent
// Parameters:
// - ctx: Context information
// - id: Unique identifier of the agent to copy
// Returns:
// - The newly created agent copy
// - Possible errors such as not existing, insufficient permissions, etc.
CopyAgent(ctx context.Context, id string) (*types.CustomAgent, error)
// GetSuggestedQuestions returns suggested questions for the agent based on its
// associated knowledge bases. When kbIDs or knowledgeIDs are provided, they override
// the agent's default knowledge base selection.
// Parameters:
// - ctx: Context information
// - agentID: Agent ID
// - kbIDs: Optional knowledge base IDs to override agent config
// - knowledgeIDs: Optional knowledge item IDs to further filter
// - tagScopes: Optional KB-scoped knowledge tags (OR semantics within each KB)
// - limit: Maximum number of questions to return
// Returns:
// - List of suggested questions
// - Possible errors
GetSuggestedQuestions(ctx context.Context, agentID string, kbIDs []string, knowledgeIDs []string, tagScopes []types.TagScope, limit int) ([]types.SuggestedQuestion, error)
// GetKnowledgeSuggestedQuestions returns only knowledge-derived candidates.
// It is independent of whether starter suggestions are enabled and is used
// as a source/fallback for contextual follow-up generation.
GetKnowledgeSuggestedQuestions(ctx context.Context, agentID string, kbIDs []string, knowledgeIDs []string, tagScopes []types.TagScope, limit int) ([]types.SuggestedQuestion, error)
}
CustomAgentService defines the custom agent service interface Provides high-level operations for agent creation, querying, updating, and deletion
type DataSourceRepository ¶
type DataSourceRepository interface {
// Create inserts a new data source record
Create(ctx context.Context, ds *types.DataSource) error
// FindByID retrieves a data source by ID
FindByID(ctx context.Context, id string) (*types.DataSource, error)
// FindByKnowledgeBase lists all data sources for a knowledge base
FindByKnowledgeBase(ctx context.Context, kbID string) ([]*types.DataSource, error)
// Update updates an existing data source
Update(ctx context.Context, ds *types.DataSource) error
// UpdateSyncState updates only fields produced by a sync run.
UpdateSyncState(ctx context.Context, ds *types.DataSource) error
// Delete performs a soft delete
Delete(ctx context.Context, id string) error
// FindActive retrieves all active data sources (used for scheduling)
FindActive(ctx context.Context) ([]*types.DataSource, error)
}
DataSourceRepository defines database access patterns for data sources
type DataSourceService ¶
type DataSourceService interface {
// CreateDataSource creates a new data source configuration
CreateDataSource(ctx context.Context, ds *types.DataSource) (*types.DataSource, error)
// GetDataSource retrieves a data source by ID
GetDataSource(ctx context.Context, id string) (*types.DataSource, error)
// ListDataSources lists all data sources for a knowledge base
ListDataSources(ctx context.Context, kbID string) ([]*types.DataSource, error)
// UpdateDataSource updates an existing data source
UpdateDataSource(ctx context.Context, ds *types.DataSource) (*types.DataSource, error)
// DeleteDataSource deletes a data source (soft delete)
DeleteDataSource(ctx context.Context, id string) error
// UpdateDataSourceCredentials replaces the connector credential map.
// DataSource credentials are per-connector atomic — there is no
// individual-field PUT, the whole map gets replaced. Returns the updated
// data source so the caller can re-fetch the redacted shape.
UpdateDataSourceCredentials(
ctx context.Context, id string, credentials map[string]interface{},
) (*types.DataSource, error)
// ClearDataSourceCredentials wipes the connector credential map.
// Idempotent on already-empty credentials.
ClearDataSourceCredentials(ctx context.Context, id string) error
// ValidateConnection tests the connection to an external data source
ValidateConnection(ctx context.Context, dsID string) error
// ValidateCredentials tests connectivity using raw credentials without persisting anything.
// This is used by the frontend "Test Connection" button before creating a data source.
ValidateCredentials(ctx context.Context, connectorType string, credentials map[string]interface{}) error
// ListAvailableResources lists resources available for sync in the external system.
// parentID enables lazy loading: "" lists the top level, a resource ExternalID lists its children.
ListAvailableResources(ctx context.Context, dsID string, parentID string) ([]types.Resource, error)
// ResolveResourceAncestors returns the deduplicated ExternalIDs of every
// ancestor that must be expanded to reveal the given (possibly deeply nested)
// resources in a lazily-loaded picker. Used to restore an existing selection
// when editing a data source.
ResolveResourceAncestors(ctx context.Context, dsID string, resourceIDs []string) ([]string, error)
// ManualSync triggers an immediate sync for a data source
ManualSync(ctx context.Context, dsID string) (*types.SyncLog, error)
// PauseDataSource pauses a data source's scheduled syncs
PauseDataSource(ctx context.Context, id string) error
// ResumeDataSource resumes a paused data source
ResumeDataSource(ctx context.Context, id string) error
// GetSyncLogs retrieves sync history for a data source
GetSyncLogs(ctx context.Context, dsID string, limit int, offset int) ([]*types.SyncLog, error)
// GetSyncLog retrieves a specific sync log entry
GetSyncLog(ctx context.Context, syncLogID string) (*types.SyncLog, error)
// ProcessSync handles the actual sync operation (called by asynq task)
ProcessSync(ctx context.Context, task *asynq.Task) error
}
DataSourceService defines the interface for data source management operations
type DatasetService ¶
type DatasetService interface {
// GetDatasetByID retrieves QA pairs from dataset by ID
GetDatasetByID(ctx context.Context, datasetID string) ([]*types.QAPair, error)
}
DatasetService defines operations for dataset management
type DocReader ¶
type DocReader interface {
Read(ctx context.Context, req *types.ReadRequest) (*types.ReadResult, error)
}
DocReader is the core interface for reading documents into markdown.
type DocumentReader ¶
type DocumentReader interface {
DocReader
Reconnect(addr string) error
IsConnected() bool
// ListEngines queries the remote docreader for its registered parser engines.
// Returns engines the remote service supports, allowing auto-discovery of
// newly added engines without Go code changes.
ListEngines(ctx context.Context, overrides map[string]string) ([]types.ParserEngineInfo, error)
}
DocumentReader extends DocReader with transport lifecycle management and remote engine discovery. Used by gRPC/HTTP clients that talk to the Python docreader service.
type EmbedChannelRepository ¶
type EmbedChannelRepository interface {
Create(ctx context.Context, ch *types.EmbedChannel) error
GetByID(ctx context.Context, id string) (*types.EmbedChannel, error)
GetByPublishToken(ctx context.Context, token string) (*types.EmbedChannel, error)
ListByAgent(ctx context.Context, tenantID uint64, agentID string) ([]*types.EmbedChannel, error)
ListByTenant(ctx context.Context, tenantID uint64) ([]*types.EmbedChannel, error)
Update(ctx context.Context, ch *types.EmbedChannel) error
Delete(ctx context.Context, tenantID uint64, id string) error
}
EmbedChannelRepository persists web embed channel rows.
type EmbedChannelService ¶
type EmbedChannelService interface {
Create(ctx context.Context, tenantID uint64, agentID string, req *types.EmbedChannel) (*types.EmbedChannel, string, error)
ListByAgent(ctx context.Context, tenantID uint64, agentID string) ([]*types.EmbedChannel, error)
ListByTenant(ctx context.Context, tenantID uint64) ([]*types.EmbedChannel, error)
Update(ctx context.Context, tenantID uint64, id string, req *types.EmbedChannel, enabled *bool, showSuggested *bool, allowWebSearch *bool, allowFileUpload *bool, defaultLocale *string, webhookURL *string, webhookSecret *string) (*types.EmbedChannel, error)
Delete(ctx context.Context, tenantID uint64, id string) error
RotateToken(ctx context.Context, tenantID uint64, id string) (*types.EmbedChannel, string, error)
LookupForEmbed(ctx context.Context, channelID, token string) (*types.EmbedChannel, error)
LookupEnabledChannel(ctx context.Context, channelID string) (*types.EmbedChannel, error)
IssueSessionToken(ctx context.Context, channelID string) (sessionToken string, expiresIn int, err error)
IssuePreviewSession(ctx context.Context, tenantID uint64, channelID string) (sessionToken string, expiresIn int, err error)
ResolveSessionToken(ctx context.Context, token string) (channelID string, err error)
PublicConfig(ctx context.Context, ch *types.EmbedChannel) types.EmbedChannelPublicConfig
SuggestedQuestions(ctx context.Context, ch *types.EmbedChannel, limit int) ([]types.SuggestedQuestion, error)
EmbedChunk(ctx context.Context, ch *types.EmbedChannel, chunkID string) (*types.Chunk, error)
EmbedDisplayTitle(ctx context.Context, ch *types.EmbedChannel) string
GetOwnedChannel(ctx context.Context, tenantID uint64, id string) (*types.EmbedChannel, error)
}
EmbedChannelService manages web embed channel lifecycle.
type EngineFactory ¶
type EngineFactory func(ctx context.Context, store types.VectorStore) (RetrieveEngineService, error)
EngineFactory creates a RetrieveEngineService from a VectorStore's config. Defined as a function type to avoid circular imports between container and service packages.
type EvalHook ¶
type EvalHook interface {
// Handle processes evaluation state change
Handle(ctx context.Context, state types.EvalState, index int, data interface{}) error
}
EvalHook defines interface for evaluation process hooks
type EvaluationService ¶
type EvaluationService interface {
// Evaluation starts a new evaluation task
Evaluation(ctx context.Context, datasetID string, knowledgeBaseID string,
chatModelID string, rerankModelID string,
) (*types.EvaluationDetail, error)
// EvaluationResult retrieves evaluation result by task ID
EvaluationResult(ctx context.Context, taskID string) (*types.EvaluationDetail, error)
}
EvaluationService defines operations for evaluation tasks
type FileService ¶
type FileService interface {
// CheckConnectivity verifies that the storage backend is reachable and
// properly configured (e.g. bucket exists, credentials valid).
CheckConnectivity(ctx context.Context) error
// SaveFile saves a file.
SaveFile(ctx context.Context, file *multipart.FileHeader, tenantID uint64, knowledgeID string) (string, error)
// SaveBytes saves bytes data to a file and returns the file path.
// If temp is true, the file will be saved to a temporary storage that may auto-expire.
SaveBytes(ctx context.Context, data []byte, tenantID uint64, fileName string, temp bool) (string, error)
// GetFile retrieves a file.
GetFile(ctx context.Context, filePath string) (io.ReadCloser, error)
// GetFileURL returns a download URL for the file (if supported by the storage backend).
GetFileURL(ctx context.Context, filePath string) (string, error)
// DeleteFile deletes a file.
DeleteFile(ctx context.Context, filePath string) error
// CopyFile copies an existing stored object to a NEW object owned by
// (tenantID, knowledgeID), returning the new provider:// path. The copy is
// independent: deleting the source never affects it. Returns ErrCrossBackendCopy
// when srcPath belongs to a different storage provider than this service.
CopyFile(ctx context.Context, srcPath string, tenantID uint64, knowledgeID string) (string, error)
}
FileService is the interface for file services. FileService provides methods to save, retrieve, and delete files.
type FilteredWebSearchProvider ¶
type FilteredWebSearchProvider interface {
SearchWithFilters(ctx context.Context, query string, maxResults int, includeDate bool,
filters types.WebSearchFilters) ([]*types.WebSearchResult, error)
}
FilteredWebSearchProvider explicitly supports per-call region and freshness filters. Providers without this capability must not silently discard requested filters.
type KBResourceLookup ¶
type KBResourceLookup interface {
IsReferencedByKnowledgeBase(ctx context.Context, tenantID uint64, kbID, reference string) (bool, error)
}
KBResourceLookup verifies an explicit binding to a live knowledge document. Text references and common storage tenancy never establish ownership.
type KBShareRepository ¶
type KBShareRepository interface {
// CRUD
DeleteByKnowledgeBaseID(ctx context.Context, kbID string) error
DeleteByOrganizationID(ctx context.Context, orgID string) error
// into an organization (e.g. when the tenant leaves or is removed)
DeleteByOrganizationAndSourceTenant(ctx context.Context, orgID string, sourceTenantID uint64) error
// List
ListSharedKBsForTenant(ctx context.Context, tenantID uint64) ([]*types.KnowledgeBaseShare, error)
// Count shares
}
KBShareRepository defines the knowledge base sharing repository interface
type KBShareService ¶
type KBShareService interface {
// Share Management
// Query
// ListSharesByKnowledgeBase lists shares for a KB; tenantID must own the KB (authz check).
// ListSharedKnowledgeBaseIDsByOrganizations returns per-org direct shared KB IDs (batch, for sidebar count).
// OrgMemberRole the caller can use against the shared KB after the
// 3-D cap, plus a bool indicating whether *any* share matches the
// caller's tenant. callerTenantRole is the caller's tenant-RBAC
// role and is used to apply the Viewer cap.
CheckTenantKBPermission(ctx context.Context, kbID string, callerTenantID uint64, callerTenantRole types.TenantRole) (types.OrgMemberRole, bool, error)
// CheckTenantKBPermission.
HasTenantKBPermission(ctx context.Context, kbID string, callerTenantID uint64, callerTenantRole types.TenantRole, requiredRole types.OrgMemberRole) (bool, error)
GetKBSourceTenant(ctx context.Context, kbID string) (uint64, error)
CountSharesByKnowledgeBaseIDs(ctx context.Context, kbIDs []string) (map[string]int64, error)
CountByOrganizations(ctx context.Context, orgIDs []string) (map[string]int64, error)
}
KBShareService defines the knowledge base sharing service interface.
Plan 3 of #1303: permission resolution keys on the caller's tenant (not user). The 3-dimension cap is applied inside CheckTenantKBPermission and is the canonical permission gate for shared KBs:
effective = min(share.Permission, tenant_org_role, tenant_role_cap)
where tenant_role_cap pins tenant Viewers to OrgRoleViewer regardless of the org-level grant — Viewer in your own tenant must always be read-only on shared resources.
type KnowledgeBaseProfileService ¶
type KnowledgeBaseProfileService interface {
// BuildAggregate computes the live aggregate (counts, samples, hash) for
// the knowledge base without calling a model.
BuildAggregate(ctx context.Context, kb *types.KnowledgeBase) (*types.KnowledgeBaseProfileAggregate, error)
// GenerateKnowledgeBaseProfile rebuilds the generated description. With
// force=false it returns the stored profile untouched when the aggregate
// hash still matches. The returned profile is also written to kb.
GenerateKnowledgeBaseProfile(
ctx context.Context, kb *types.KnowledgeBase, force bool,
) (*types.KnowledgeBaseProfile, error)
// RequestKnowledgeBaseProfileRefresh enqueues an asynchronous rebuild.
// It is a no-op when automatic generation is disabled unless force is set.
RequestKnowledgeBaseProfileRefresh(ctx context.Context, kb *types.KnowledgeBase, force bool) error
// Handle is the asynq entry point for types.TypeKnowledgeBaseProfile.
Handle(ctx context.Context, task *asynq.Task) error
}
KnowledgeBaseProfileService derives a knowledge-base description from the structured profiles of its documents. The aggregation is deterministic and cheap; only the final short text costs one small model call, and that call is skipped when the aggregate has not changed since the last generation.
type KnowledgeBaseRepository ¶
type KnowledgeBaseRepository interface {
// CreateKnowledgeBase creates a knowledge base record
// Parameters:
// - ctx: Context information
// - kb: Knowledge base object
// Returns:
// - Possible errors such as database connection failure, unique constraint conflicts, etc.
CreateKnowledgeBase(ctx context.Context, kb *types.KnowledgeBase) error
// GetKnowledgeBaseByID queries a knowledge base by ID
// Parameters:
// - ctx: Context information
// - id: Knowledge base ID
// Returns:
// - Knowledge base object, if found
// - Possible errors such as record not existing, database errors, etc.
GetKnowledgeBaseByID(ctx context.Context, id string) (*types.KnowledgeBase, error)
// GetKnowledgeBaseByIDAndTenant queries a knowledge base by ID scoped to a tenant.
// Returns ErrKnowledgeBaseNotFound if the KB does not exist or does not belong to the tenant.
// Parameters:
// - ctx: Context information
// - id: Knowledge base ID
// - tenantID: Tenant ID (enforces tenant isolation)
// Returns:
// - Knowledge base object, if found and owned by tenant
// - Possible errors such as record not existing or wrong tenant, database errors, etc.
GetKnowledgeBaseByIDAndTenant(ctx context.Context, id string, tenantID uint64) (*types.KnowledgeBase, error)
// GetKnowledgeBaseByIDs queries knowledge bases by multiple IDs
// Parameters:
// - ctx: Context information
// - ids: List of knowledge base IDs
// Returns:
// - List of knowledge base objects
// - Possible errors such as database errors, etc.
GetKnowledgeBaseByIDs(ctx context.Context, ids []string) ([]*types.KnowledgeBase, error)
// ListKnowledgeBases lists all knowledge bases in the system
// Parameters:
// - ctx: Context information
// Returns:
// - List of knowledge base objects
// - Possible errors such as database errors, etc.
ListKnowledgeBases(ctx context.Context) ([]*types.KnowledgeBase, error)
// ListKnowledgeBasesByTenantID lists all knowledge bases for a specific tenant
// Parameters:
// - ctx: Context information
// - tenantID: Tenant ID
// Returns:
// - List of knowledge base objects
// - Possible errors such as database errors, etc.
ListKnowledgeBasesByTenantID(ctx context.Context, tenantID uint64) ([]*types.KnowledgeBase, error)
// UpdateKnowledgeBase updates a knowledge base record
// Parameters:
// - ctx: Context information
// - kb: Knowledge base object containing update information
// Returns:
// - Possible errors such as record not existing, database errors, etc.
UpdateKnowledgeBase(ctx context.Context, kb *types.KnowledgeBase) error
// DeleteKnowledgeBase deletes a knowledge base record
// Parameters:
// - ctx: Context information
// - id: Knowledge base ID
// Returns:
// - Possible errors such as record not existing, database errors, etc.
DeleteKnowledgeBase(ctx context.Context, id string) error
// CountByVectorStoreID counts active KBs bound to the given vector store
// within a tenant scope. Accepts a *gorm.DB handle so callers can share a
// transaction (e.g., the VectorStore delete guard's row-lock context) or
// run standalone (pass nil → uses the repository's default db).
//
// The soft-delete filter is applied automatically by the gorm.DeletedAt
// scope on KnowledgeBase; implementations MUST NOT add an explicit
// `deleted_at IS NULL` predicate (avoids divergence with the auto-scope).
CountByVectorStoreID(ctx context.Context, db *gorm.DB, tenantID uint64, storeID string) (int64, error)
// CountByModelID counts active KBs in the tenant that reference the given
// model ID in any model-binding field (embedding, summary, VLM, ASR, etc.).
CountByModelID(ctx context.Context, tenantID uint64, modelID string) (int64, error)
// ListModelUsages returns the minimal active KB projections that reference
// the model, with every matching binding merged per object. Implementations
// must cap the result at types.ModelUsageListLimit; callers that need the
// untruncated size should use CountByModelID.
ListModelUsages(ctx context.Context, tenantID uint64, modelID string) ([]types.ModelUsageResource, error)
// SetUserKBPin inserts or removes a row in user_kb_pins for the given
// (tenant, user, kb) triple. Returns the resulting pinned_at (nil when
// pinned=false) and an error. The tenant_id is captured to support
// efficient "wipe a tenant" cleanups even though (user_id, kb_id)
// alone would be unique in practice.
SetUserKBPin(
ctx context.Context, tenantID uint64, userID string, kbID string, pinned bool,
) (pinnedAt *time.Time, err error)
// UpdateKnowledgeBaseGeneratedProfile writes only the generated_profile
// column so a background regeneration never races a concurrent settings
// save on the rest of the row.
UpdateKnowledgeBaseGeneratedProfile(ctx context.Context, id string, profile *types.KnowledgeBaseProfile) error
// ListUserKBPinIDs returns the kb_id → pinned_at map of every KB the
// given user has personally pinned in this tenant. Used by the list
// path to stamp KnowledgeBase.IsPinned / PinnedAt without a per-row
// roundtrip.
ListUserKBPinIDs(
ctx context.Context, tenantID uint64, userID string,
) (map[string]time.Time, error)
}
KnowledgeBaseRepository defines the knowledge base repository interface Responsible for knowledge base data persistence and retrieval, serving as a bridge between the service layer and data storage
type KnowledgeBaseService ¶
type KnowledgeBaseService interface {
// CreateKnowledgeBase creates a new knowledge base
// Parameters:
// - ctx: Context information, carrying request tracking, user identity, etc.
// - kb: Knowledge base object containing basic information
// Returns:
// - Created knowledge base object (including automatically generated ID)
// - Possible errors such as insufficient permissions, duplicate names, etc.
CreateKnowledgeBase(ctx context.Context, kb *types.KnowledgeBase) (*types.KnowledgeBase, error)
// GetKnowledgeBaseByID retrieves knowledge base information by ID
// Parameters:
// - ctx: Context information
// - id: Unique identifier of the knowledge base
// Returns:
// - Knowledge base object, if found
// - Possible errors such as not existing, insufficient permissions, etc.
GetKnowledgeBaseByID(ctx context.Context, id string) (*types.KnowledgeBase, error)
// GetKnowledgeBaseByIDOnly retrieves knowledge base by ID without tenant filter
// Used for cross-tenant shared KB access where permission is checked elsewhere
// Parameters:
// - ctx: Context information
// - id: Unique identifier of the knowledge base
// Returns:
// - Knowledge base object, if found
// - Possible errors such as not existing, etc.
GetKnowledgeBaseByIDOnly(ctx context.Context, id string) (*types.KnowledgeBase, error)
// GetKnowledgeBasesByIDsOnly retrieves knowledge bases by IDs without tenant filter (batch).
GetKnowledgeBasesByIDsOnly(ctx context.Context, ids []string) ([]*types.KnowledgeBase, error)
// FillKnowledgeBaseCounts fills KnowledgeCount, ChunkCount, IsProcessing, ProcessingCount for the given KB (uses kb.TenantID).
FillKnowledgeBaseCounts(ctx context.Context, kb *types.KnowledgeBase) error
// ListKnowledgeBases lists all knowledge bases under the current tenant
// Parameters:
// - ctx: Context information, containing tenant information
// Returns:
// - List of knowledge base objects
// - Possible errors such as insufficient permissions, etc.
ListKnowledgeBases(ctx context.Context) ([]*types.KnowledgeBase, error)
// ListKnowledgeBasesByTenantID lists all knowledge bases for a specific tenant (e.g. for shared agent context).
ListKnowledgeBasesByTenantID(ctx context.Context, tenantID uint64) ([]*types.KnowledgeBase, error)
// UpdateKnowledgeBase updates knowledge base information
// Parameters:
// - ctx: Context information
// - id: Unique identifier of the knowledge base
// - name: New knowledge base name
// - description: New knowledge base description
// - config: Knowledge base configuration, including chunking strategy, vectorization settings, etc.
// - vlmConfig: Optional multimodal (vision) config; nil means no change
// Returns:
// - Updated knowledge base object
// - Possible errors such as not existing, insufficient permissions, etc.
UpdateKnowledgeBase(ctx context.Context,
id string, name string, description string, config *types.KnowledgeBaseConfig, vlmConfig *types.VLMConfig,
) (*types.KnowledgeBase, error)
// DeleteKnowledgeBase deletes a knowledge base
// Parameters:
// - ctx: Context information
// - id: Unique identifier of the knowledge base
// Returns:
// - Possible errors such as not existing, insufficient permissions, etc.
DeleteKnowledgeBase(ctx context.Context, id string) error
// TogglePinKnowledgeBase toggles the pin status of a knowledge base
TogglePinKnowledgeBase(ctx context.Context, id string) (*types.KnowledgeBase, error)
// HybridSearch performs hybrid search (vector + keywords) in the knowledge base
// Parameters:
// - ctx: Context information
// - id: Unique identifier of the knowledge base
// - params: Search parameters, including query text, thresholds, etc.
// Returns:
// - List of search results, sorted by relevance
// - Possible errors such as not existing, insufficient permissions, search engine errors, etc.
HybridSearch(ctx context.Context, id string, params types.SearchParams) ([]*types.SearchResult, error)
// HybridSearchWithRerank is HybridSearch plus the optional rerank stage
// requested by params.Rerank, returning rerank diagnostics beside the
// results. It serves the hybrid-search API; internal callers rerank on
// their own and use HybridSearch.
HybridSearchWithRerank(ctx context.Context, id string, params types.SearchParams) (*types.RetrievalResult, error)
// GetQueryEmbedding computes the query embedding using the embedding model
// associated with the given knowledge base. This allows callers to pre-compute
// and reuse embeddings across multiple KBs that share the same model.
GetQueryEmbedding(ctx context.Context, kbID string, queryText string) ([]float32, error)
// ResolveEmbeddingModelKeys resolves embedding model IDs to their actual
// model identity key (name + endpoint). KBs using the same underlying model
// across different tenants will share the same key, enabling optimal grouping.
// Returns a map from KB ID to model identity key string.
ResolveEmbeddingModelKeys(ctx context.Context, kbs []*types.KnowledgeBase) map[string]string
// CopyKnowledgeBase copies a knowledge base
// Parameters:
// - ctx: Context information
// - sourceID: Source knowledge base ID
// - targetID: Target knowledge base ID
// Returns:
// - Copied knowledge base object
// - Possible errors such as not existing, insufficient permissions, etc.
CopyKnowledgeBase(ctx context.Context, src string, dst string) (*types.KnowledgeBase, *types.KnowledgeBase, error)
// DuplicateKnowledgeBase creates a new settings-only knowledge base duplicate.
// It does not copy knowledge entries, chunks, FAQ rows, wiki pages, indexes,
// data sources, shares, pins, or task state. A new UUID is always generated.
DuplicateKnowledgeBase(ctx context.Context, src string) (*types.KnowledgeBase, error)
// GetRepository gets the knowledge base repository
// Parameters:
// - ctx: Context with authentication and request information
//
// Returns:
// - interfaces.KnowledgeBaseRepository: Knowledge base repository
GetRepository() KnowledgeBaseRepository
// ProcessKBDelete handles async knowledge base deletion task
// Parameters:
// - ctx: Context information
// - t: Asynq task containing KBDeletePayload
// Returns:
// - Possible errors during deletion
ProcessKBDelete(ctx context.Context, t *asynq.Task) error
// ReadChunkImage reads the image a search result shows (the first image
// of its image_info) from the storage of its knowledge base. The result
// must come from a search the caller was authorized to run.
ReadChunkImage(ctx context.Context, result *types.SearchResult) ([]byte, error)
}
KnowledgeBaseService defines the knowledge base service interface Provides high-level operations for knowledge base creation, querying, updating, deletion, and content searching
type KnowledgeBaseTaskCanceller ¶
type KnowledgeBaseTaskCanceller interface {
// CancelTasksForKnowledgeBase removes pending/scheduled/retry tasks
// associated with kbID and signals matching active workers to stop.
// knowledgeIDs covers batch tasks whose payload references documents but
// does not carry the parent knowledge-base ID. dataSourceIDs covers sync
// tasks whose payload only identifies a data source.
CancelTasksForKnowledgeBase(
ctx context.Context,
kbID string,
knowledgeIDs []string,
dataSourceIDs []string,
) (deleted int, cancelled int, err error)
}
KnowledgeBaseTaskCanceller is the optional queue-cleanup capability used when a knowledge base is deleted. It is separate from TaskInspector so lightweight test doubles and queue backends that cannot inspect tasks do not need to implement knowledge-base-wide scanning.
type KnowledgeIndexMover ¶
type KnowledgeIndexMover interface {
MoveKnowledgeIndices(
ctx context.Context,
sourceKB, targetKB, knowledgeID string,
chunkIDs []string,
dimension int,
knowledgeType string,
) error
}
KnowledgeIndexMover changes the KB binding of existing indices, preserving their chunk IDs and vectors. It must match both source KB and document, clear KB-scoped tags, and be safe to repeat after a partial failure. CopyIndices followed by DeleteByKnowledgeIDList cannot implement this: the unchanged knowledge ID also selects the destination rows for deletion.
type KnowledgeRepository ¶
type KnowledgeRepository interface {
CreateKnowledge(ctx context.Context, knowledge *types.Knowledge) error
GetKnowledgeByID(ctx context.Context, tenantID uint64, id string) (*types.Knowledge, error)
// GetKnowledgeByIDOnly returns knowledge by ID without tenant filter (for permission resolution).
GetKnowledgeByIDOnly(ctx context.Context, id string) (*types.Knowledge, error)
ListKnowledgeByKnowledgeBaseID(ctx context.Context, tenantID uint64, kbID string) ([]*types.Knowledge, error)
// ListPagedKnowledgeByKnowledgeBaseID lists all knowledge in a knowledge base
// with pagination. The filter struct controls optional dimensions (tag, keyword,
// file type, parse status, source channel, updated time range); pass a zero
// struct to disable all filters.
ListPagedKnowledgeByKnowledgeBaseID(ctx context.Context,
tenantID uint64, kbID string, page *types.Pagination, filter types.KnowledgeListFilter,
) ([]*types.Knowledge, int64, error)
UpdateKnowledge(ctx context.Context, knowledge *types.Knowledge) error
// UpdateKnowledgeForTransfer conditionally persists a transfer checkpoint
// and its storage delta in one transaction, without inserting missing rows.
UpdateKnowledgeForTransfer(ctx context.Context, before, after *types.Knowledge) error
// UpdateKnowledgeBatch updates knowledge items in batch
UpdateKnowledgeBatch(ctx context.Context, knowledgeList []*types.Knowledge) error
DeleteKnowledge(ctx context.Context, tenantID uint64, id string) error
DeleteKnowledgeList(ctx context.Context, tenantID uint64, ids []string) error
GetKnowledgeBatch(ctx context.Context, tenantID uint64, ids []string) ([]*types.Knowledge, error)
// GetKnowledgeBatchByIDOnly returns knowledge by IDs without tenant filter
// (for shared-KB resolution; callers check permissions on every row).
GetKnowledgeBatchByIDOnly(ctx context.Context, ids []string) ([]*types.Knowledge, error)
// CheckKnowledgeExists checks if knowledge already exists.
// For file types, check by fileHash or (fileName+fileSize).
// For URL types, check by URL.
// Returns whether it exists, the existing knowledge object (if any), and possible error.
CheckKnowledgeExists(
ctx context.Context,
tenantID uint64,
kbID string,
params *types.KnowledgeCheckParams,
) (bool, *types.Knowledge, error)
// ListKnowledgeFolderCounts aggregates the number of knowledge entries
// stored directly in each folder_path of a knowledge base.
ListKnowledgeFolderCounts(
ctx context.Context,
tenantID uint64,
kbID string,
) ([]*types.KnowledgeFolderCount, error)
// UpdateKnowledgeFolderPath files the given entries under folderPath.
UpdateKnowledgeFolderPath(
ctx context.Context,
tenantID uint64,
kbID string,
ids []string,
folderPath string,
) (int64, error)
// RenameKnowledgeFolderPath rewrites folder_path for a folder and all of its
// descendants. Renaming onto an existing path merges the folders.
RenameKnowledgeFolderPath(
ctx context.Context,
tenantID uint64,
kbID string,
from string,
to string,
) (int64, error)
// AminusB returns the IDs of knowledge in A that have no counterpart in B,
// comparing file_hash as a multiset (so duplicate-count differences and
// NULL/empty hashes are handled correctly, letting a clone converge).
AminusB(ctx context.Context, Atenant uint64, A string, Btenant uint64, B string) ([]string, error)
UpdateKnowledgeColumn(ctx context.Context, id string, column string, value interface{}) error
// UpdateKnowledgeColumns updates multiple columns of a knowledge row in a single
// statement so callers that flip several related fields (e.g. parse_status +
// error_message) cannot leave the row in a half-updated state.
UpdateKnowledgeColumns(ctx context.Context, id string, values map[string]interface{}) error
// UpdateActiveDeletingKnowledgeColumns updates an active, non-deleted knowledge row
// only when it is still in the transient deleting state.
UpdateActiveDeletingKnowledgeColumns(
ctx context.Context,
tenantID uint64,
kbID, id string,
values map[string]interface{},
) (bool, error)
// FinalizeSubtask atomically decrements pending_subtasks_count for the
// given knowledge and promotes parse_status from "finalizing" to
// "completed" when the count reaches zero. Returns the post-decrement
// count, whether this caller's UPDATE was the one that promoted the
// row, and any error.
FinalizeSubtask(ctx context.Context, id string) (int, bool, error)
// SetFinalizing atomically transitions a row from "processing" to
// "finalizing" and writes the initial pending_subtasks_count. Returns
// whether the transition took place (false when the row's parse_status
// was no longer "processing", e.g. user cancelled / deleted in flight).
SetFinalizing(ctx context.Context, id string, expectedSubtasks int) (bool, error)
// CompleteProcessingWithoutSubtasks atomically completes a still-processing
// document that has no enrichment tasks, without overriding cancel/delete.
CompleteProcessingWithoutSubtasks(ctx context.Context, id string) (bool, error)
// CountKnowledgeByKnowledgeBaseID counts the number of knowledge items in a knowledge base.
CountKnowledgeByKnowledgeBaseID(ctx context.Context, tenantID uint64, kbID string) (int64, error)
// CountKnowledgeByStatus counts the number of knowledge items with the specified parse status.
CountKnowledgeByStatus(ctx context.Context, tenantID uint64, kbID string, parseStatuses []string) (int64, error)
// SearchKnowledge searches knowledge items by keyword across the tenant.
// fileTypes: optional list of file extensions to filter by (e.g., ["csv", "xlsx"])
SearchKnowledge(ctx context.Context, tenantID uint64, keyword string, offset, limit int, fileTypes []string) ([]*types.Knowledge, bool, error)
// FindByMetadataKey finds a knowledge item by a key-value pair in the metadata JSON column.
// Used by data source sync to locate existing items by external_id.
FindByMetadataKey(ctx context.Context, tenantID uint64, kbID string, key string, value string) (*types.Knowledge, error)
// FindByMetadataKeyPrefix finds knowledge items whose metadata[key] starts
// with the given prefix. Used to sweep an external node's attachment sub-items.
FindByMetadataKeyPrefix(ctx context.Context, tenantID uint64, kbID string, key string, prefix string) ([]*types.Knowledge, error)
// FindByDataSourceExternalID finds a knowledge item owned by one data source
// and identified by the source's external item ID.
FindByDataSourceExternalID(
ctx context.Context, tenantID uint64, kbID, dataSourceID, externalID string,
) (*types.Knowledge, error)
// HardDeleteKnowledge physically removes a row after DeleteKnowledge's soft-delete
// cascade. Sync-internal deletions use this so rows never become tombstones.
HardDeleteKnowledge(ctx context.Context, tenantID uint64, id string) error
// HardDeleteKnowledgeList is the batch counterpart of HardDeleteKnowledge.
HardDeleteKnowledgeList(ctx context.Context, tenantID uint64, ids []string) error
// SearchKnowledgeInScopes searches knowledge items by keyword within the given (tenant_id, kb_id) scopes (own + shared).
SearchKnowledgeInScopes(ctx context.Context, scopes []types.KnowledgeSearchScope, keyword string, offset, limit int, fileTypes []string) ([]*types.Knowledge, bool, int64, error)
// ListIDsByTagIDs returns all knowledge IDs that have any of the specified tag IDs (OR semantics).
ListIDsByTagIDs(ctx context.Context, tenantID uint64, kbID string, tagIDs []string) ([]string, error)
// SetKnowledgeTags replaces all tags for a single knowledge entry (deletes old, inserts new).
SetKnowledgeTags(ctx context.Context, knowledgeID string, tagIDs []string) error
// AddKnowledgeTagRelations attaches tags without removing existing ones,
// after validating that the knowledge and every tag belong to the given
// tenant and knowledge base. Duplicate deliveries are idempotent.
AddKnowledgeTagRelations(
ctx context.Context,
tenantID uint64,
kbID, knowledgeID string,
tagIDs []string,
) error
// GetKnowledgeTags returns tags for multiple knowledge IDs.
GetKnowledgeTags(ctx context.Context, knowledgeIDs []string) (map[string][]*types.KnowledgeTag, error)
// DeleteKnowledgeTagRelations deletes all tag relations for a knowledge entry.
DeleteKnowledgeTagRelations(ctx context.Context, knowledgeID string) error
// ListKnowledgeProfileRows returns the lightweight projection used to
// aggregate a knowledge-base description: every enabled document that has
// finished parsing (completed or finalizing), without content columns.
ListKnowledgeProfileRows(ctx context.Context, tenantID uint64, kbID string) ([]*types.KnowledgeProfileRow, error)
}
KnowledgeRepository defines the interface for knowledge repositories.
type KnowledgeService ¶
type KnowledgeService interface {
// CreateKnowledgeFromFile creates knowledge from a file.
// channel identifies the ingestion channel (e.g. "web", "api", "wechat"); empty defaults to "web".
CreateKnowledgeFromFile(
ctx context.Context,
kbID string,
file *multipart.FileHeader,
metadata map[string]string,
enableMultimodel *bool,
customFileName string,
tagIDs []string,
channel string,
processOverrides *types.KnowledgeProcessOverrides,
) (*types.Knowledge, error)
// CreateKnowledgeFromURL creates knowledge from a URL.
// When fileName or fileType is provided (or the URL path has a known file extension),
// the URL is treated as a direct file download instead of a web page crawl.
// channel identifies the ingestion channel; empty defaults to "web".
CreateKnowledgeFromURL(
ctx context.Context,
kbID string,
url string,
fileName string,
fileType string,
enableMultimodel *bool,
title string,
tagIDs []string,
channel string,
processOverrides *types.KnowledgeProcessOverrides,
) (*types.Knowledge, error)
// CreateKnowledgeFromPassage creates knowledge from text passages.
// channel identifies the ingestion channel; empty defaults to "web".
CreateKnowledgeFromPassage(ctx context.Context, kbID string, passage []string, channel string) (*types.Knowledge, error)
// CreateKnowledgeFromPassageSync creates knowledge from text passages and waits until chunks are indexed.
CreateKnowledgeFromPassageSync(ctx context.Context, kbID string, passage []string, channel string) (*types.Knowledge, error)
// CreateKnowledgeFromManual creates or saves manual Markdown knowledge content.
// channel identifies the ingestion channel; empty defaults to "web".
CreateKnowledgeFromManual(
ctx context.Context,
kbID string,
payload *types.ManualKnowledgePayload,
channel string,
) (*types.Knowledge, error)
// GetKnowledgeByID retrieves knowledge by ID (uses tenant from context).
GetKnowledgeByID(ctx context.Context, id string) (*types.Knowledge, error)
// GetKnowledgeByIDOnly retrieves knowledge by ID without tenant filter (for permission resolution).
GetKnowledgeByIDOnly(ctx context.Context, id string) (*types.Knowledge, error)
// GetOwningKBCreatorID resolves a knowledge ID to the CreatorID of its
// owning KnowledgeBase, scoped to the caller's tenant. Used by the
// per-KB ownership lookups in handler/rbac_lookups.go (PR 5, #1303) so
// chunk and knowledge sub-resource routes can inherit the same
// "creator-of-the-KB OR Admin+" gate that KB-level routes already use.
// Returns the underlying repository sentinel errors unchanged so
// callers can map them to middleware.ErrResourceNotFound.
GetOwningKBCreatorID(ctx context.Context, knowledgeID string) (string, error)
// GetKnowledgeBatch retrieves a batch of knowledge by IDs.
GetKnowledgeBatch(ctx context.Context, tenantID uint64, ids []string) ([]*types.Knowledge, error)
GetKnowledgeBatchWithSharedAccess(ctx context.Context, tenantID uint64, ids []string) ([]*types.Knowledge, error)
// ListKnowledgeIDsByTagIDs returns document knowledge IDs carrying any of
// the specified KB-local tags.
ListKnowledgeIDsByTagIDs(ctx context.Context, tenantID uint64, kbID string, tagIDs []string) ([]string, error)
// ListKnowledgeByKnowledgeBaseID lists all knowledge under a knowledge base.
ListKnowledgeByKnowledgeBaseID(ctx context.Context, kbID string) ([]*types.Knowledge, error)
// ListPagedKnowledgeByKnowledgeBaseID lists all knowledge under a knowledge base
// with pagination. The filter struct controls optional dimensions (tag, keyword,
// file type, parse status, source channel, updated time range); pass a zero
// struct to disable all filters.
ListPagedKnowledgeByKnowledgeBaseID(
ctx context.Context,
kbID string,
page *types.Pagination,
filter types.KnowledgeListFilter,
) (*types.PageResult, error)
// ListKnowledgeFolderTree returns the folder hierarchy derived from the
// folder_path of every knowledge entry in a knowledge base, with per-folder
// document counts. It powers the document sidebar tree.
ListKnowledgeFolderTree(ctx context.Context, kbID string) (*types.KnowledgeFolderTree, error)
// MoveKnowledgeToFolder re-files knowledge entries under the given folder
// path (empty means the knowledge base top level). Folders are derived from
// the stored paths, so a path that does not exist yet is created implicitly.
MoveKnowledgeToFolder(
ctx context.Context,
kbID string,
ids []string,
folderPath string,
) (int64, error)
// RenameKnowledgeFolder moves a folder and everything below it to a new path.
RenameKnowledgeFolder(ctx context.Context, kbID string, from string, to string) (int64, error)
// DeleteKnowledge deletes knowledge by ID.
DeleteKnowledge(ctx context.Context, id string) error
// DeleteKnowledgeList requires an explicit write grant for every affected KB.
// It validates the complete selection before changing any deletion state.
DeleteKnowledgeList(ctx context.Context, ids []string) error
// GetKnowledgeFile retrieves the file associated with the knowledge.
GetKnowledgeFile(ctx context.Context, id string) (io.ReadCloser, string, error)
// UpdateKnowledge updates knowledge information.
UpdateKnowledge(ctx context.Context, knowledge *types.Knowledge) error
// RegenerateKnowledgeSummary refreshes the document description and summary retrieval chunk.
RegenerateKnowledgeSummary(ctx context.Context, knowledgeID string) (*types.Knowledge, error)
// RequestKnowledgeSummaryRefresh enqueues an async summary refresh.
RequestKnowledgeSummaryRefresh(ctx context.Context, knowledgeID string) error
// RegenerateChunkQuestions rebuilds the auxiliary questions for one current chunk revision.
RegenerateChunkQuestions(ctx context.Context, chunkID string) ([]types.GeneratedQuestion, error)
// UpdateManualKnowledge updates manual Markdown knowledge content.
UpdateManualKnowledge(
ctx context.Context,
knowledgeID string,
payload *types.ManualKnowledgePayload,
) (*types.Knowledge, error)
// ReparseKnowledge deletes existing document content and re-parses the knowledge asynchronously.
// When processOverrides is non-nil, it is validated and persisted to the knowledge metadata
// before re-parsing, letting callers adjust parse config on reparse; nil keeps stored overrides.
ReparseKnowledge(
ctx context.Context,
knowledgeID string,
processOverrides *types.KnowledgeProcessOverrides,
) (*types.Knowledge, error)
// ReplaceKnowledgeFile replaces the source file of an existing file knowledge
// while preserving its ID, then re-parses it. A path-qualified customFileName
// also sets the folder; a bare filename keeps the current folder. Metadata
// entries are merged into the stored metadata. Identical content on this
// same path returns a DuplicateKnowledgeError without re-parsing.
ReplaceKnowledgeFile(
ctx context.Context,
knowledgeID string,
file *multipart.FileHeader,
customFileName string,
metadata map[string]string,
) (*types.Knowledge, error)
// CancelKnowledgeParse marks an in-progress parse as cancelled by the
// user. The knowledge row and any partially written chunks/index are
// kept; downstream queued tasks for the same knowledge are best-effort
// dequeued and active workers are signalled to stop at their next
// checkpoint. Idempotent — returns the existing row when the knowledge
// is already cancelled. Returns an error when the knowledge is in a
// terminal state (completed / failed) or being deleted.
CancelKnowledgeParse(ctx context.Context, knowledgeID string) (*types.Knowledge, error)
// CloneKnowledgeBase clones knowledge to another knowledge base.
CloneKnowledgeBase(ctx context.Context, srcID, dstID string) error
// UpdateImageInfo updates image information for a knowledge chunk.
UpdateImageInfo(ctx context.Context, knowledgeID string, chunkID string, imageInfo string) error
// ListFAQEntries lists FAQ entries under a FAQ knowledge base.
// When tagUUIDs is non-empty, results are filtered by tag UUID on FAQ chunks (OR semantics).
// searchField: specifies which field to search in ("standard_question", "similar_questions", "answers", "" for all)
// sortOrder: "asc" for time ascending (updated_at ASC), default is time descending (updated_at DESC)
ListFAQEntries(
ctx context.Context,
kbID string,
page *types.Pagination,
tagUUIDs []string,
legacyTagSeqID int64,
keyword string,
searchField string,
sortOrder string,
isEnabled *bool,
) (*types.PageResult, error)
// UpsertFAQEntries imports or appends FAQ entries asynchronously.
// When DryRun is true, only validates entries without actually importing.
// Returns task ID (Knowledge ID) for tracking import progress.
UpsertFAQEntries(ctx context.Context, kbID string, payload *types.FAQBatchUpsertPayload) (string, error)
// CreateFAQEntry creates a single FAQ entry synchronously.
CreateFAQEntry(ctx context.Context, kbID string, payload *types.FAQEntryPayload) (*types.FAQEntry, error)
// GetFAQEntry retrieves a single FAQ entry by seq_id.
GetFAQEntry(ctx context.Context, kbID string, entrySeqID int64) (*types.FAQEntry, error)
// UpdateFAQEntry updates a single FAQ entry.
UpdateFAQEntry(ctx context.Context, kbID string, entrySeqID int64, payload *types.FAQEntryPayload) (*types.FAQEntry, error)
// AddSimilarQuestions adds similar questions to a FAQ entry.
AddSimilarQuestions(ctx context.Context, kbID string, entrySeqID int64, questions []string) (*types.FAQEntry, error)
// UpdateFAQEntryFieldsBatch updates multiple fields for FAQ entries in batch.
// Supports updating is_enabled, is_recommended, tag_id, and other fields in a single call.
UpdateFAQEntryFieldsBatch(ctx context.Context, kbID string, req *types.FAQEntryFieldsBatchUpdate) error
// DeleteFAQEntries deletes FAQ entries in batch by seq_id.
DeleteFAQEntries(ctx context.Context, kbID string, entrySeqIDs []int64) error
// SearchFAQEntries searches FAQ entries using hybrid search.
SearchFAQEntries(ctx context.Context, kbID string, req *types.FAQSearchRequest) ([]*types.FAQEntry, error)
// ExportFAQEntries exports all FAQ entries for a knowledge base as CSV data.
ExportFAQEntries(ctx context.Context, kbID string) ([]byte, error)
// ExportFAQEntriesJSON exports all FAQ entries as a JSON array compatible with FAQEntryPayload.
ExportFAQEntriesJSON(ctx context.Context, kbID string) ([]byte, error)
// UpdateKnowledgeTagBatch updates tag for document knowledge items in batch.
// authorizedKBID restricts all updates to knowledge items belonging to this KB;
// an empty value still requires an explicit write grant for every affected KB.
UpdateKnowledgeTagBatch(ctx context.Context, authorizedKBID string, updates map[string][]string) error
// SetKnowledgeTags replaces all tags for a single knowledge entry.
SetKnowledgeTags(ctx context.Context, knowledgeID string, tagIDs []string) error
// GetKnowledgeTags returns tags for multiple knowledge IDs.
GetKnowledgeTags(ctx context.Context, knowledgeIDs []string) (map[string][]*types.KnowledgeTag, error)
// UpdateFAQEntryTagBatch updates tag for FAQ entries in batch.
// Key: entry seq_id, Value: tag seq_id (nil to remove tag)
UpdateFAQEntryTagBatch(ctx context.Context, kbID string, updates map[int64]*int64) error
// GetRepository gets the knowledge repository
GetRepository() KnowledgeRepository
// ProcessManualUpdate handles Asynq manual knowledge update tasks (cleanup + re-indexing)
ProcessManualUpdate(ctx context.Context, t *asynq.Task) error
// ProcessDocument handles Asynq document processing tasks
ProcessDocument(ctx context.Context, t *asynq.Task) error
// ProcessFAQImport handles Asynq FAQ import tasks
ProcessFAQImport(ctx context.Context, t *asynq.Task) error
// ProcessQuestionGeneration handles Asynq question generation tasks
ProcessQuestionGeneration(ctx context.Context, t *asynq.Task) error
// ProcessSummaryGeneration handles Asynq summary generation tasks
ProcessSummaryGeneration(ctx context.Context, t *asynq.Task) error
// ProcessKBClone handles Asynq knowledge base clone tasks
ProcessKBClone(ctx context.Context, t *asynq.Task) error
// ProcessKnowledgeMove handles Asynq knowledge move tasks
ProcessKnowledgeMove(ctx context.Context, t *asynq.Task) error
// ProcessKnowledgeListDelete handles Asynq knowledge list delete tasks
ProcessKnowledgeListDelete(ctx context.Context, t *asynq.Task) error
// ProcessKnowledgeListReparse handles Asynq knowledge list reparse tasks
ProcessKnowledgeListReparse(ctx context.Context, t *asynq.Task) error
// GetKBCloneProgress retrieves the progress of a knowledge base clone task
GetKBCloneProgress(ctx context.Context, taskID string) (*types.KBCloneProgress, error)
// SaveKBCloneProgress saves the progress of a knowledge base clone task
SaveKBCloneProgress(ctx context.Context, progress *types.KBCloneProgress) error
// GetKnowledgeMoveProgress retrieves the progress of a knowledge move task
GetKnowledgeMoveProgress(ctx context.Context, taskID string) (*types.KnowledgeMoveProgress, error)
// SaveKnowledgeMoveProgress saves the progress of a knowledge move task
SaveKnowledgeMoveProgress(ctx context.Context, progress *types.KnowledgeMoveProgress) error
// GetFAQImportProgress retrieves the progress of an FAQ import task
GetFAQImportProgress(ctx context.Context, taskID string) (*types.FAQImportProgress, error)
// UpdateLastFAQImportResultDisplayStatus updates the display status of FAQ import result
UpdateLastFAQImportResultDisplayStatus(ctx context.Context, kbID string, displayStatus string) error
// SearchKnowledge searches knowledge items by keyword across the tenant.
// fileTypes: optional list of file extensions to filter by (e.g., ["csv", "xlsx"])
SearchKnowledge(ctx context.Context, keyword string, offset, limit int, fileTypes []string) ([]*types.Knowledge, bool, int64, error)
// SearchKnowledgeForScopes searches knowledge within the given (tenant_id, kb_id) scopes (e.g. for shared agent context).
SearchKnowledgeForScopes(ctx context.Context, scopes []types.KnowledgeSearchScope, keyword string, offset, limit int, fileTypes []string) ([]*types.Knowledge, bool, int64, error)
}
KnowledgeService defines the interface for knowledge services.
type KnowledgeTagRepository ¶
type KnowledgeTagRepository interface {
Create(ctx context.Context, tag *types.KnowledgeTag) error
Update(ctx context.Context, tag *types.KnowledgeTag) error
GetByID(ctx context.Context, tenantID uint64, id string) (*types.KnowledgeTag, error)
// GetBySeqID retrieves a tag by its seq_id.
GetBySeqID(ctx context.Context, tenantID uint64, seqID int64) (*types.KnowledgeTag, error)
// GetByIDs retrieves multiple tags by their IDs in a single query.
GetByIDs(ctx context.Context, tenantID uint64, ids []string) ([]*types.KnowledgeTag, error)
// GetBySeqIDs retrieves multiple tags by their seq_ids in a single query.
GetBySeqIDs(ctx context.Context, tenantID uint64, seqIDs []int64) ([]*types.KnowledgeTag, error)
GetByName(ctx context.Context, tenantID uint64, kbID string, name string) (*types.KnowledgeTag, error)
ListByKB(
ctx context.Context,
tenantID uint64,
kbID string,
page *types.Pagination,
keyword string,
) ([]*types.KnowledgeTag, int64, error)
Delete(ctx context.Context, tenantID uint64, id string) error
// CountReferences returns number of knowledges and chunks that reference the tag.
CountReferences(
ctx context.Context,
tenantID uint64,
kbID string,
tagID string,
) (knowledgeCount int64, chunkCount int64, err error)
// BatchCountReferences returns number of knowledges and chunks for multiple tags in a single query.
// Returns a map of tagID -> {knowledgeCount, chunkCount}
BatchCountReferences(
ctx context.Context,
tenantID uint64,
kbID string,
tagIDs []string,
) (map[string]types.TagReferenceCounts, error)
// DeleteUnusedTags deletes tags that are not referenced by any knowledge or chunk.
DeleteUnusedTags(ctx context.Context, tenantID uint64, kbID string) (int64, error)
}
KnowledgeTagRepository defines persistence operations for tags.
type KnowledgeTagService ¶
type KnowledgeTagService interface {
// ListTags lists all tags under a knowledge base with associated statistics.
ListTags(ctx context.Context, kbID string, page *types.Pagination, keyword string) (*types.PageResult, error)
// CreateTag creates a new tag under a knowledge base.
CreateTag(ctx context.Context, kbID string, name string, color string, sortOrder int) (*types.KnowledgeTag, error)
// UpdateTag updates tag basic information.
UpdateTag(ctx context.Context, id string, name *string, color *string, sortOrder *int) (*types.KnowledgeTag, error)
// DeleteTag deletes a tag.
// When contentOnly=true, only deletes the content under the tag but keeps the tag itself.
// excludeIDs: IDs of chunks to exclude from deletion (only valid when deleting chunks)
DeleteTag(ctx context.Context, id string, force bool, contentOnly bool, excludeIDs []string) error
// FindOrCreateTagByName finds a tag by name or creates it if not exists.
FindOrCreateTagByName(ctx context.Context, kbID string, name string) (*types.KnowledgeTag, error)
// ProcessIndexDelete handles async index deletion task
ProcessIndexDelete(ctx context.Context, t *asynq.Task) error
}
KnowledgeTagService defines operations on knowledge base scoped tags.
type MCPEndpointRepository ¶
type MCPEndpointRepository interface {
Create(ctx context.Context, ep *types.MCPEndpoint) error
GetByID(ctx context.Context, id string) (*types.MCPEndpoint, error)
GetByTokenHash(ctx context.Context, tokenHash string) (*types.MCPEndpoint, error)
ListByTenant(ctx context.Context, tenantID uint64) ([]*types.MCPEndpoint, error)
Update(ctx context.Context, ep *types.MCPEndpoint) error
Delete(ctx context.Context, tenantID uint64, id string) error
TouchLastUsed(ctx context.Context, id string) error
}
MCPEndpointRepository persists workspace MCP endpoints.
type MCPEndpointService ¶
type MCPEndpointService interface {
// Create stores a new endpoint and returns it together with the one-time
// plaintext token.
Create(ctx context.Context, tenantID uint64, ep *types.MCPEndpoint) (*types.MCPEndpoint, string, error)
Get(ctx context.Context, tenantID uint64, id string) (*types.MCPEndpoint, error)
List(ctx context.Context, tenantID uint64) ([]*types.MCPEndpoint, error)
Update(ctx context.Context, tenantID uint64, id string, upd MCPEndpointUpdate) (*types.MCPEndpoint, error)
Delete(ctx context.Context, tenantID uint64, id string) error
// RotateToken replaces the bearer token and returns the new plaintext.
RotateToken(ctx context.Context, tenantID uint64, id string) (*types.MCPEndpoint, string, error)
// Authenticate resolves a bearer token presented on the MCP surface to an
// enabled endpoint whose ID matches. It returns ErrMCPEndpointNotFound or
// ErrMCPEndpointDisabled style sentinels on failure.
Authenticate(ctx context.Context, endpointID, token string) (*types.MCPEndpoint, error)
}
MCPEndpointService manages the lifecycle of workspace MCP endpoints and resolves bearer tokens for the MCP server surface.
type MCPEndpointUpdate ¶
type MCPEndpointUpdate struct {
Name *string
Description *string
Enabled *bool
KnowledgeBaseIDs *[]string
Tools *[]string
DefaultAgentID *string
RateLimitPerMinute *int
}
MCPEndpointUpdate carries the optional fields of an endpoint update. Nil means "leave unchanged" so a partial PUT does not reset a field.
type MCPMetadataRepository ¶
type MCPMetadataRepository interface {
GetMetadata(context.Context, uint64, string, string) (*types.MCPMetadata, error)
ListMetadataSummaries(context.Context, uint64, []string) ([]*types.MCPMetadataSummary, error)
SaveMetadata(context.Context, *types.MCPMetadata) error
}
MCPMetadataRepository persists principal-scoped directory snapshots.
type MCPMetadataService ¶
type MCPMetadataService interface {
// GetMCPMetadata reads only persisted metadata; nil means never synchronized.
GetMCPMetadata(context.Context, uint64, string) (*types.MCPMetadata, error)
// ListMCPMetadataSummaries returns persisted directory counts for the list UI.
ListMCPMetadataSummaries(context.Context, uint64, []*types.MCPService) (map[string]*types.MCPMetadataSummary, error)
// PersistMCPMetadata stores a complete directory already listed on an authorized connection.
PersistMCPMetadata(context.Context, uint64, string, []*types.MCPTool, string) error
// RefreshMCPMetadata explicitly connects and atomically replaces a complete snapshot.
RefreshMCPMetadata(context.Context, uint64, string) (*types.MCPMetadata, error)
}
MCPMetadataService reads and explicitly refreshes directory snapshots.
type MCPOAuthRepository ¶
type MCPOAuthRepository interface {
// GetClient returns the registered OAuth client for a service, or
// (nil, nil) when none has been registered yet.
GetClient(ctx context.Context, tenantID uint64, serviceID string) (*types.MCPOAuthClient, error)
// SaveClient creates or updates the registered OAuth client for a service.
SaveClient(ctx context.Context, client *types.MCPOAuthClient) error
// DeleteClient removes the registered OAuth client for a service.
DeleteClient(ctx context.Context, tenantID uint64, serviceID string) error
// GetToken returns the stored token for (tenant, user, service), or
// (nil, nil) when the user has not authorized yet.
GetToken(ctx context.Context, tenantID uint64, userID, serviceID string) (*types.MCPOAuthToken, error)
// GetTokenForPrincipal returns the stored token for (tenant, principal, service),
// or (nil, nil) when the principal has not authorized yet.
GetTokenForPrincipal(
ctx context.Context, tenantID uint64, principal types.Principal, serviceID string,
) (*types.MCPOAuthToken, error)
// SaveToken creates or updates the per-user token for a service.
SaveToken(ctx context.Context, token *types.MCPOAuthToken) error
// SaveTokenForPrincipal creates or updates the per-principal token for a service.
SaveTokenForPrincipal(ctx context.Context, token *types.MCPOAuthToken) error
// DeleteToken removes the per-user token for a service (revoke /
// re-authorize).
DeleteToken(ctx context.Context, tenantID uint64, userID, serviceID string) error
// DeleteTokenForPrincipal removes the per-principal token for a service.
DeleteTokenForPrincipal(ctx context.Context, tenantID uint64, principal types.Principal, serviceID string) error
// TryAcquireTokenRefreshLease atomically claims the right to use a refresh
// token. This prevents concurrent instances from presenting the same
// rotating refresh token to the authorization server.
TryAcquireTokenRefreshLease(
ctx context.Context,
tenantID uint64,
principal types.Principal,
serviceID, leaseID string,
leaseUntil time.Time,
) (bool, error)
// ReleaseTokenRefreshLease releases a lease only when leaseID still owns it.
ReleaseTokenRefreshLease(
ctx context.Context,
tenantID uint64,
principal types.Principal,
serviceID, leaseID string,
) error
}
MCPOAuthRepository persists OAuth clients (per service) and tokens (per principal + service) for the MCP OAuth2 authorization-code flow.
type MCPServiceRepository ¶
type MCPServiceRepository interface {
// Create creates a new MCP service
Create(ctx context.Context, service *types.MCPService) error
// GetByID retrieves an MCP service by ID and tenant ID
GetByID(ctx context.Context, tenantID uint64, id string) (*types.MCPService, error)
// List retrieves all MCP services for a tenant
List(ctx context.Context, tenantID uint64) ([]*types.MCPService, error)
// ListEnabled retrieves all enabled MCP services for a tenant
ListEnabled(ctx context.Context, tenantID uint64) ([]*types.MCPService, error)
// ListByIDs retrieves MCP services by multiple IDs for a tenant
ListByIDs(ctx context.Context, tenantID uint64, ids []string) ([]*types.MCPService, error)
// Update updates an MCP service
Update(ctx context.Context, service *types.MCPService) error
// Delete deletes an MCP service (soft delete)
Delete(ctx context.Context, tenantID uint64, id string) error
}
MCPServiceRepository defines the interface for MCP service data access
type MCPServiceService ¶
type MCPServiceService interface {
// CreateMCPService creates a new MCP service
CreateMCPService(ctx context.Context, service *types.MCPService) error
// GetMCPServiceByID retrieves an MCP service by ID
GetMCPServiceByID(ctx context.Context, tenantID uint64, id string) (*types.MCPService, error)
// ListMCPServices lists all MCP services for a tenant
ListMCPServices(ctx context.Context, tenantID uint64) ([]*types.MCPService, error)
// ListMCPMetadataSummaries returns persisted directory counts for the list UI.
ListMCPMetadataSummaries(context.Context, uint64, []*types.MCPService) (map[string]*types.MCPMetadataSummary, error)
// ListMCPServicesByIDs retrieves multiple MCP services by IDs
ListMCPServicesByIDs(ctx context.Context, tenantID uint64, ids []string) ([]*types.MCPService, error)
// UpdateMCPService updates an MCP service. updateFields records presence for
// scalar fields whose zero values cannot represent omission. Supported keys
// are "name", "description", "usage_instructions", "enabled", "auth_type",
// and "api_key_header"; a nil map means none of those
// scalar fields were provided.
UpdateMCPService(
ctx context.Context,
service *types.MCPService,
updateFields map[string]bool,
) error
// DeleteMCPService deletes an MCP service
DeleteMCPService(ctx context.Context, tenantID uint64, id string) error
// TestMCPService tests the connection to an MCP service and returns available tools/resources
TestMCPService(ctx context.Context, tenantID uint64, id string) (*types.MCPTestResult, error)
// GetMCPServiceTools retrieves the list of tools from an MCP service
GetMCPServiceTools(ctx context.Context, tenantID uint64, id string) ([]*types.MCPTool, error)
// GetMCPServiceResources retrieves the list of resources from an MCP service
GetMCPServiceResources(ctx context.Context, tenantID uint64, id string) ([]*types.MCPResource, error)
// UpdateMCPCredentials writes one or more credential fields on the auth
// config. Nil pointer means "do not touch this field". Returns the updated
// service (with current AuthConfig) so the handler can derive the
// configured/not-configured metadata for the response.
//
// Implementations MUST close any active MCP client connection for this
// service so the next upstream call reconnects with the new credential.
UpdateMCPCredentials(
ctx context.Context, tenantID uint64, id string, apiKey, token, clientSecret *string,
) (*types.MCPService, error)
// ClearMCPCredential removes a single credential field. field must be
// "api_key", "token" or "client_secret"; other values must be rejected by
// the caller.
// Implementations MUST close any active MCP client connection for this
// service. Clearing a field that is already empty is a no-op (no error).
ClearMCPCredential(ctx context.Context, tenantID uint64, id, field string) error
}
MCPServiceService defines the interface for MCP service business logic
type MCPToolApprovalRepository ¶
type MCPToolApprovalRepository interface {
ListByService(ctx context.Context, tenantID uint64, serviceID string) ([]*types.MCPToolApproval, error)
IsRequired(ctx context.Context, tenantID uint64, serviceID, toolName string) (bool, error)
IsEnabled(ctx context.Context, tenantID uint64, serviceID, toolName string) (bool, error)
UpsertPolicy(ctx context.Context, tenantID uint64, serviceID, toolName string, patch types.MCPToolPolicyPatch) error
}
MCPToolApprovalRepository persists per-tool MCP policies.
type MCPToolApprovalService ¶
type MCPToolApprovalService interface {
ListByService(ctx context.Context, tenantID uint64, serviceID string) ([]*types.MCPToolApproval, error)
SetPolicy(ctx context.Context, tenantID uint64, serviceID, toolName string, requireApproval, enabled *bool) error
SetRequireApproval(ctx context.Context, tenantID uint64, serviceID, toolName string, require bool) error
SetEnabled(ctx context.Context, tenantID uint64, serviceID, toolName string, enabled bool) error
IsRequired(ctx context.Context, tenantID uint64, serviceID, toolName string) (bool, error)
IsEnabled(ctx context.Context, tenantID uint64, serviceID, toolName string) (bool, error)
}
MCPToolApprovalService is the business layer for MCP tool policies.
type MemoryExtractionFailure ¶
type MemoryExtractionFailure struct {
Session types.MemoryExtractionSession
End types.MemoryMessageCursor
Code string
}
MemoryExtractionFailure identifies an invalid-output range without retaining message text.
type MemoryRecall ¶
type MemoryRecall struct {
// Prompt is the ready-to-append envelope, empty when nothing was recalled.
Prompt string
// Items are the situational items plus the resident items that produced
// Prompt, in the order they appear. Surfaced to the chat UI.
Items []*types.MemoryItem
}
MemoryRecall is what one turn pulls in: the resident block plus any situational items matched against the query.
type MemoryRepository ¶
type MemoryRepository interface {
// GetSubject returns the memory space, or (nil, nil) when it does not exist.
GetSubject(ctx context.Context, scope MemoryScope) (*types.MemorySubject, error)
// EnsureSubject returns the memory space, creating it on first use.
EnsureSubject(ctx context.Context, scope MemoryScope) (*types.MemorySubject, error)
// UpdateSubjectEnabled flips the per-user opt out.
UpdateSubjectEnabled(ctx context.Context, scope MemoryScope, enabled bool) error
// UpdateSubjectBlock stores the rendered resident block and item count.
UpdateSubjectBlock(ctx context.Context, scope MemoryScope, block string, itemCount int) error
// EnqueuePendingSession records that a session has turns past the cursor
// and claims the in-flight slot when no run is already scheduled. It
// returns the subject as it was before the update plus whether this call
// is the one responsible for enqueuing the task, so a burst of turns
// produces exactly one task and never a dropped turn.
EnqueuePendingSession(
ctx context.Context, scope MemoryScope, sessionID string, inFlightTimeout time.Duration,
) (*types.MemorySubject, bool, error)
// ClaimPendingSessions leases a snapshot without removing durable work.
// A nil batch means no work remains; RetryAt defers a busy lease.
ClaimPendingSessions(ctx context.Context, scope MemoryScope, fallbackSession, leaseID string, ttl time.Duration) (*types.MemoryExtractionBatch, error)
// CheckpointExtraction acknowledges a processed segment (or a recorded
// skip). A concurrent enqueue changes Revision and keeps the session pending.
CheckpointExtraction(ctx context.Context, scope MemoryScope, leaseID string, session types.MemoryExtractionSession, cursor types.MemoryMessageCursor, drained bool) error
HasPendingExtraction(ctx context.Context, scope MemoryScope) (bool, error)
// RecordExtractionFailure returns true after the bounded invalid-output
// retry budget. It preserves a failure range without storing transcript text.
RecordExtractionFailure(
ctx context.Context, scope MemoryScope, leaseID string, session MemoryExtractionFailure,
) (bool, error)
FinishExtraction(ctx context.Context, scope MemoryScope, leaseID string) error
// Empty leaseID only releases a queued task, never a running worker.
ReleaseExtractionSlot(ctx context.Context, scope MemoryScope, leaseID string) error
// SaveItem atomically inserts and resolves active/pending replacements.
SaveItem(ctx context.Context, scope MemoryScope, item *types.MemoryItem, replacesID string) error
// ConfirmPendingItem atomically activates a proposal and retires its target.
ConfirmPendingItem(ctx context.Context, scope MemoryScope, id string) error
// CreateItem inserts one memory item.
CreateItem(ctx context.Context, item *types.MemoryItem) error
// GetItem returns one item inside the scope, or (nil, nil) when absent.
GetItem(ctx context.Context, scope MemoryScope, id string) (*types.MemoryItem, error)
// ListActiveByKinds returns active items of the given kinds, newest first.
ListActiveByKinds(ctx context.Context, scope MemoryScope, kinds []string, limit int) ([]*types.MemoryItem, error)
// ListActiveResident returns the items that belong in the always-injected
// block: stable traits, plus anything the user explicitly asked to keep.
ListActiveResident(ctx context.Context, scope MemoryScope, limit int) ([]*types.MemoryItem, error)
// ListItems returns items for the memory manager, filtered by status.
ListItems(ctx context.Context, scope MemoryScope, status string, limit, offset int) ([]*types.MemoryItem, int64, error)
// FindActiveByKey returns the active item occupying a topic key, if any.
FindActiveByKey(ctx context.Context, scope MemoryScope, normalizedKey string) (*types.MemoryItem, error)
// UpdateItemContent rewrites an item edited in the memory manager.
UpdateItemContent(ctx context.Context, scope MemoryScope, id, content, normalizedKey string, importance int) error
// SupersedeItem marks an outdated item as replaced by another one.
SupersedeItem(ctx context.Context, scope MemoryScope, id, supersededBy string) error
// DeleteItem physically removes one item. Forgetting means forgetting.
DeleteItem(ctx context.Context, scope MemoryScope, id string) error
// DeleteAll removes every item in the scope and returns how many were removed.
DeleteAll(ctx context.Context, scope MemoryScope) (int64, error)
// TouchUsed records that items were injected into a turn.
TouchUsed(ctx context.Context, scope MemoryScope, ids []string) error
// AddTombstone records that a statement was deliberately forgotten. Only
// the topic, a fingerprint and the originating message are stored, never
// the statement itself.
AddTombstone(ctx context.Context, scope MemoryScope, topic, fingerprint, sourceMessageID string) error
// HasTombstoneForMessage reports whether a memory derived from this message
// was rejected within the given window. The window matters: this rule
// exists to stop the re-derivation that happens one debounce later, not to
// ban a message for good.
HasTombstoneForMessage(
ctx context.Context, scope MemoryScope, sourceMessageID string, within time.Duration,
) (bool, error)
// ListTombstones returns the most recent rejections for this subject.
ListTombstones(ctx context.Context, scope MemoryScope, limit int) ([]*types.MemoryTombstone, error)
// HasTombstone reports whether this exact statement was already forgotten.
HasTombstone(ctx context.Context, scope MemoryScope, fingerprint string) (bool, error)
// ExpireOverdue archives items whose expires_at has passed and returns how
// many were archived.
ExpireOverdue(ctx context.Context, scope MemoryScope) (int64, error)
// MarkConsolidated records that this subject's whole store was just
// reviewed, so the next review waits out the interval.
MarkConsolidated(ctx context.Context, scope MemoryScope) error
// MarkForcedConsolidated records that this subject asked for a review
// themselves. Kept apart from MarkConsolidated so the daily pass and the
// button rate limit each other independently.
MarkForcedConsolidated(ctx context.Context, scope MemoryScope) error
// UpsertItemEmbedding stores or replaces the vector for one memory.
UpsertItemEmbedding(ctx context.Context, scope MemoryScope, embedding *types.MemoryItemEmbedding) error
// DeleteItemEmbedding drops one memory's vector. What a memory embeds to
// can change after it is written — an interest embeds the other wordings
// of its subject — and dropping the vector is how the existing backfill is
// asked to rebuild it.
DeleteItemEmbedding(ctx context.Context, scope MemoryScope, itemID string) error
// ItemEmbeddings loads the vectors for the given items, keyed by item id.
// Only vectors produced by modelID are returned: vectors from a different
// model are not comparable, so mixing them would score nonsense.
ItemEmbeddings(ctx context.Context, scope MemoryScope, itemIDs []string, modelID string) (map[string][]float32, error)
// SearchItemsByVector returns this subject's semantically closest items,
// ranked by the database over every vector it holds.
//
// It replaces "list some items, then score the ones that happened to be
// listed". That order was the bug: candidates were chosen by importance,
// which says nothing about whether an item answers the question, so a
// well-matching memory outside the listed window could not be found at all.
SearchItemsByVector(ctx context.Context, scope MemoryScope, query MemoryVectorQuery) ([]MemoryVectorHit, error)
// SyncVectorColumn copies stored vectors into the database's own vector
// type for rows written before there was one. Returns how many it moved,
// and 0 on a deployment that scores in process. No model calls: the vector
// already exists, only its representation is behind.
SyncVectorColumn(ctx context.Context, scope MemoryScope, limit int) (int, error)
// ItemsMissingEmbeddings returns active items that have no vector yet, so a
// background pass can fill them in.
ItemsMissingEmbeddings(ctx context.Context, scope MemoryScope, modelID string, limit int) ([]*types.MemoryItem, error)
// ListLive returns items of one kind that the user can see: in use plus
// proposed and awaiting a decision.
ListLive(ctx context.Context, scope MemoryScope, kind string, limit int) ([]*types.MemoryItem, error)
// BumpTopic records one more sighting of a topic and returns the running
// total, so a caller can decide whether it has recurred enough to promote.
BumpTopic(
ctx context.Context, scope MemoryScope, topic, normalizedKey, alias string,
) (*types.MemoryTopicStat, error)
// RenameTopic gives a subject a better canonical label, keeping the old one
// as an alias. Reports false when another row already holds the new key.
RenameTopic(ctx context.Context, scope MemoryScope, oldKey, newKey, newLabel string) (bool, error)
// MarkTopicPromoted stops a topic from being promoted again.
MarkTopicPromoted(ctx context.Context, scope MemoryScope, normalizedKey string) error
// TopTopics returns the most-asked topics, newest activity first.
TopTopics(ctx context.Context, scope MemoryScope, limit int) ([]*types.MemoryTopicStat, error)
// TopicByKey returns one subject's statistics, or nil when it is not
// tracked. It exists so an interest can be embedded together with the
// other wordings this person has used for the same subject.
TopicByKey(ctx context.Context, scope MemoryScope, normalizedKey string) (*types.MemoryTopicStat, error)
// TopicByID returns one topic row inside the scope, or (nil, nil) when it
// is absent. Used by the memory manager so a user can promote or dismiss
// a subject that has not yet become a memory.
TopicByID(ctx context.Context, scope MemoryScope, id string) (*types.MemoryTopicStat, error)
// ListUnpromotedTopics returns subjects that have been counted but have
// not yet become an interest, closest to the threshold first.
ListUnpromotedTopics(ctx context.Context, scope MemoryScope, limit, offset int) ([]*types.MemoryTopicStat, int64, error)
// DeleteTopic removes one topic row. Counting starts over if the subject
// is asked about again, unless a tombstone blocks promotion.
DeleteTopic(ctx context.Context, scope MemoryScope, id string) error
// DeleteAllTopics drops every topic counter in the scope. Clearing memory
// has to include these, or a subject sitting at N-1 would promote on the
// next question after the user asked to forget everything.
DeleteAllTopics(ctx context.Context, scope MemoryScope) error
// BumpDocAffinity records that an answer for this person drew on a document.
BumpDocAffinity(ctx context.Context, scope MemoryScope, docs []types.MemoryDocAffinity) error
// DocAffinity returns this person's affinity for the given documents.
DocAffinity(ctx context.Context, scope MemoryScope, knowledgeIDs []string) (map[string]int, error)
// TopDocAffinity returns the documents this person relies on most.
TopDocAffinity(ctx context.Context, scope MemoryScope, limit int) ([]*types.MemoryDocAffinity, error)
// DocAffinityByID returns one affinity row inside the scope, or (nil, nil)
// when it is absent.
DocAffinityByID(ctx context.Context, scope MemoryScope, id string) (*types.MemoryDocAffinity, error)
// ListFamiliarDocs returns documents cited often enough to count as a
// habit, most-used first.
ListFamiliarDocs(ctx context.Context, scope MemoryScope, minHits, limit, offset int) ([]*types.MemoryDocAffinity, int64, error)
// DeleteDocAffinity removes one document counter.
DeleteDocAffinity(ctx context.Context, scope MemoryScope, id string) error
// DeleteAllDocAffinity drops every document counter in the scope.
DeleteAllDocAffinity(ctx context.Context, scope MemoryScope) error
// ArchiveLowestRanked archives active items beyond the capacity cap and
// returns how many were archived.
ArchiveLowestRanked(ctx context.Context, scope MemoryScope, keep int) (int64, error)
// CountActive returns the number of active items in the scope.
CountActive(ctx context.Context, scope MemoryScope) (int64, error)
}
MemoryRepository is the storage contract for long-term memory. Every method takes an explicit scope rather than reading it from ctx, so a background worker cannot accidentally operate on whatever the ambient context happens to hold.
type MemoryScope ¶
MemoryScope is the resolved (workspace, principal) pair a memory operation runs under. It is always derived from the request context so no caller can address another user's memory by passing an id.
func (MemoryScope) Valid ¶
func (s MemoryScope) Valid() bool
type MemorySearchResult ¶
type MemorySearchResult struct {
// Available is false when memory is off at any level — workspace, user or
// the agent handling this request.
Available bool
// Items are the matches, most relevant first.
Items []*types.MemoryItem
}
MemorySearchResult is what an on-demand lookup into the memory store returns.
It is a separate type from MemoryRecall because the two have different consumers. Recall produces a prompt envelope for a turn; a search produces items for a tool, and that tool has to tell the model *why* it got nothing. "This user has memory switched off" and "nothing stored matches" call for different answers, and collapsing both into an empty slice would have the agent report a blank memory store to someone who simply disabled it.
type MemoryService ¶
type MemoryService interface {
// Recall assembles the memory to inject for one turn. It performs no LLM
// calls and returns an empty recall (never an error) whenever memory is
// disabled at any level, so callers can use it unconditionally.
Recall(ctx context.Context, query string) MemoryRecall
// SearchMemory ranks the user's stored memories against an arbitrary
// query, for callers that need to reach past what Recall's per-turn budget
// admitted. Like Recall it performs no LLM call and never errors.
SearchMemory(ctx context.Context, query string, limit int) MemorySearchResult
// MemoryAvailable reports whether this request may read memory at all:
// the workspace switch, the user's own opt out and the agent's preference
// combined.
//
// Read paths do not need this — Recall and SearchMemory already degrade on
// their own. It exists for callers that must decide whether to *offer* a
// memory-backed feature, where the difference between "off" and "empty"
// has to be settled before anything is built rather than after it is
// called.
MemoryAvailable(ctx context.Context) bool
// RetrievalContextFor returns what memory contributes to retrieval. Like
// Recall it makes no model call and degrades to an empty value.
RetrievalContextFor(ctx context.Context) RetrievalContext
// DocumentAffinity scores the given documents by how much this person has
// relied on them before. Absent documents simply have no entry.
DocumentAffinity(ctx context.Context, knowledgeIDs []string) map[string]int
// RecordAnswerSources notes which documents an answer drew on, which is the
// only per-person retrieval signal available without asking anything.
RecordAnswerSources(ctx context.Context, refs []types.MemoryDocAffinity)
// ObserveQuestionTopics counts what a person asked about so a recurring
// subject can become an interest. Returns any newly promoted interests.
ObserveQuestionTopics(ctx context.Context, topics []string) []string
// Remember stores a statement the user explicitly asked to keep.
Remember(ctx context.Context, item types.MemoryItem) (*types.MemoryItem, error)
// ScheduleExtraction debounces and enqueues background distillation for a
// finished turn. Best effort: failures are logged, never returned.
ScheduleExtraction(ctx context.Context, sessionID, messageID, chatModelID string)
// Handle runs the background distillation task.
Handle(ctx context.Context, task *asynq.Task) error
// ListItems backs the memory manager list.
ListItems(ctx context.Context, status string, limit, offset int) ([]*types.MemoryItem, int64, error)
// ListTopics returns subjects that have been counted but not yet promoted
// into an interest, so the memory manager can show what is still forming.
ListTopics(ctx context.Context, limit, offset int) ([]*types.MemoryTopicView, int64, error)
// PromoteTopic turns a counted subject into an interest immediately,
// without waiting for the remaining hits.
PromoteTopic(ctx context.Context, id string) (*types.MemoryItem, error)
// DeleteTopic stops tracking a subject. The refusal is remembered so the
// same topic is not promoted automatically later.
DeleteTopic(ctx context.Context, id string) error
// ListDocuments returns documents this person keeps drawing answers from.
ListDocuments(ctx context.Context, limit, offset int) ([]*types.MemoryDocView, int64, error)
// DeleteDocument stops using one document as a personal retrieval signal.
DeleteDocument(ctx context.Context, id string) error
// FamiliarKnowledgeIDs returns the knowledge ids that have been cited
// often enough to count as a habit. Used to highlight Wiki pages whose
// source documents this person actually works from. Empty on any failure
// so callers can use it unconditionally.
FamiliarKnowledgeIDs(ctx context.Context) []string
// CreateItem adds a memory typed by the user in the memory manager.
CreateItem(ctx context.Context, kind, content string, importance int) (*types.MemoryItem, error)
// ConfirmItem accepts a memory the system inferred, so it starts being used.
ConfirmItem(ctx context.Context, id string) (*types.MemoryItem, error)
// RejectItem declines an inference and remembers the refusal, so the same
// guess is not proposed again next week.
RejectItem(ctx context.Context, id string) error
// UpdateItem edits one item's content and importance.
UpdateItem(ctx context.Context, id, content string, importance int) (*types.MemoryItem, error)
// DeleteItem forgets one item.
DeleteItem(ctx context.Context, id string) error
// Clear forgets everything in the caller's memory space.
Clear(ctx context.Context) (int64, error)
// ConsolidateNow reviews the caller's store for near-duplicates and stale
// tasks immediately, without waiting for the daily distillation pass.
ConsolidateNow(ctx context.Context) (*types.MemoryConsolidationResult, error)
// GetSettings returns the effective per-user memory settings.
GetSettings(ctx context.Context) (*types.MemorySettings, error)
// SetEnabled flips the per-user opt out.
SetEnabled(ctx context.Context, enabled bool) error
}
MemoryService is the read/write API for long-term memory.
type MemoryVectorHit ¶
type MemoryVectorHit struct {
Item *types.MemoryItem
Score float64
}
MemoryVectorHit is one semantic match and how close it was.
type MemoryVectorQuery ¶
type MemoryVectorQuery struct {
// ModelID pins the vector space. Vectors from another model are skipped
// rather than scored, because the distance between them is meaningless.
ModelID string
// Vector is the embedded query.
Vector []float32
// Kinds restricts which memories may come back. Empty means every kind.
Kinds []string
// MinScore is the cosine floor below which a match is not a match.
MinScore float64
// Limit is how many hits to return, best first.
Limit int
}
MemoryVectorQuery is one semantic lookup over a subject's stored vectors.
type MessageFileBindingLookup ¶
type MessageFileBindingLookup interface {
GetMessageFileBindings(
ctx context.Context,
tenantID uint64,
reference, messageID string,
) (*types.MessageFileBindings, error)
}
MessageFileBindingLookup resolves explicit KB and message-artifact bindings. Only the catalog can turn a raw locator into a registered resource identity.
type MessageRepository ¶
type MessageRepository interface {
// CreateMessage creates a message
CreateMessage(ctx context.Context, message *types.Message) (*types.Message, error)
// GetMessage gets a message
GetMessage(ctx context.Context, sessionID string, id string) (*types.Message, error)
// GetMessagesBySession gets all messages of a session
GetMessagesBySession(ctx context.Context, sessionID string, page int, pageSize int) ([]*types.Message, error)
// GetRecentMessagesBySession gets recent messages of a session
GetRecentMessagesBySession(ctx context.Context, sessionID string, limit int) ([]*types.Message, error)
// GetMessagesBySessionBeforeTime gets messages before a specific time of a session
GetMessagesBySessionBeforeTime(
ctx context.Context, sessionID string, beforeTime time.Time, limit int,
) ([]*types.Message, error)
// ListMessagesBySessionAfterTime returns messages created strictly after
// afterTime, oldest first. Long-term memory distillation walks forward from
// a watermark, so it needs the oldest unprocessed messages rather than the
// newest ones: paging from the newest end would skip everything in between
// once a session outruns the page size.
ListMessagesBySessionAfterTime(
ctx context.Context, sessionID string, afterTime time.Time, limit int,
) ([]*types.Message, error)
// ListMessagesBySessionAfterCursor uses (created_at, id) for lossless paging.
ListMessagesBySessionAfterCursor(ctx context.Context, sessionID string, cursor types.MemoryMessageCursor, limit int) ([]*types.Message, error)
// ListMessagesBySessionBeforeCursor pages a session backwards: up to limit
// messages sorting strictly before (before, beforeID), newest first. A zero
// cursor starts from the newest message.
ListMessagesBySessionBeforeCursor(
ctx context.Context, sessionID string, before time.Time, beforeID string, limit int,
) ([]*types.Message, error)
// ListMessagesBySessionUpTo returns every message sorting strictly before
// the (boundary, boundaryID) composite cursor, oldest first. Used by
// session fork to copy the history preceding a fork point.
ListMessagesBySessionUpTo(
ctx context.Context, sessionID string, boundary time.Time, boundaryID string,
) ([]*types.Message, error)
// UpdateMessage updates a message
UpdateMessage(ctx context.Context, message *types.Message) error
// UpdateMessageImages updates only the images JSONB column for a message
UpdateMessageImages(ctx context.Context, sessionID, messageID string, images types.MessageImages) error
// UpdateMessageRenderedContent updates the rendered_content column for a user message
UpdateMessageRenderedContent(ctx context.Context, sessionID, messageID string, renderedContent string) error
// UpdateMessageContextCheckpoint writes only the context_checkpoint column
// of an assistant message.
UpdateMessageContextCheckpoint(
ctx context.Context, sessionID, messageID string, checkpoint *types.ContextCheckpoint,
) error
// GetLatestContextCheckpoint returns the newest assistant message in the
// session that carries a context checkpoint, projected to the columns
// history loading needs, or nil when there is none.
GetLatestContextCheckpoint(ctx context.Context, sessionID string) (*types.Message, error)
// DeleteMessage deletes a message
DeleteMessage(ctx context.Context, sessionID string, id string) error
// DeleteMessagesFrom deletes every message at or after the
// (boundary, boundaryID) composite cursor and returns the deleted rows,
// oldest first. inclusive keeps or drops the boundary message itself,
// which is what separates a user rewind point from an assistant one.
DeleteMessagesFrom(
ctx context.Context, sessionID string, boundary time.Time, boundaryID string, inclusive bool,
) ([]*types.Message, error)
// DeleteMessagesBySessionID deletes all messages belonging to a session
DeleteMessagesBySessionID(ctx context.Context, sessionID string) error
// GetFirstMessageOfUser gets the first message of a user
GetFirstMessageOfUser(ctx context.Context, sessionID string) (*types.Message, error)
// SearchMessagesByKeyword searches messages by keyword across sessions for a tenant
// OwnedSessionIDs narrows a set of session ids to the ones this person owns.
OwnedSessionIDs(ctx context.Context, tenantID uint64, ownerID string, sessionIDs []string) (map[string]bool, error)
SearchMessagesByKeyword(ctx context.Context, tenantID uint64, ownerID, keyword string, sessionIDs []string, limit int) ([]*types.MessageWithSession, error)
// GetMessagesByKnowledgeIDs retrieves messages by their associated Knowledge IDs
GetMessagesByKnowledgeIDs(ctx context.Context, knowledgeIDs []string) ([]*types.MessageWithSession, error)
// GetMessagesByRequestIDs retrieves messages by request ID inside one session
// (used to fetch Q&A pair partners). Empty sessionID returns no rows.
GetMessagesByRequestIDs(
ctx context.Context, sessionID string, requestIDs []string,
) ([]*types.MessageWithSession, error)
// GetKnowledgeIDsBySessionID retrieves all knowledge IDs for messages in a session
GetKnowledgeIDsBySessionID(ctx context.Context, sessionID string) ([]string, error)
// UpdateMessageKnowledgeID updates the knowledge_id field for a message
UpdateMessageKnowledgeID(ctx context.Context, messageID string, knowledgeID string) error
// GetSessionArtifacts returns every skill-produced MessageArtifact recorded
// against any assistant message of the session, in creation order. The
// implementation only projects the artifacts JSONB column, so it stays
// cheap even for long conversations. Empty slice + nil error means the
// session has no artifacts yet (never an error).
GetSessionArtifacts(ctx context.Context, sessionID string) (types.MessageArtifacts, error)
// ListArtifactLibrary returns one page of the latest artifact versions in
// the sessions query.TenantID / query.UserID can see, plus the total.
ListArtifactLibrary(
ctx context.Context, query *types.ArtifactLibraryQuery,
) ([]*types.ArtifactLibraryItem, int64, error)
// FindSessionArtifact returns the live artifact row addressed by
// (session, message, position), or nil when there is none. A tombstoned
// row reads as absent.
FindSessionArtifact(
ctx context.Context, sessionID, messageID string, position int,
) (*types.MessageArtifactRecord, error)
// FindSessionArtifactVersions returns every live row of the session sharing
// sourcePath — the regenerations the artifact library folds into one entry.
// An empty sourcePath yields nothing: such artifacts have no version group.
FindSessionArtifactVersions(
ctx context.Context, sessionID, sourcePath string,
) ([]types.MessageArtifactRecord, error)
// SoftDeleteSessionArtifacts tombstones the referenced rows and returns the
// subset it marked, leaving out rows another caller already tombstoned.
SoftDeleteSessionArtifacts(
ctx context.Context, sessionID string, refs []types.ArtifactRef, at time.Time,
) ([]types.ArtifactRef, error)
// CountLiveArtifactsByURL counts undeleted artifact rows pointing at a
// stored object, across every session. Callers use it as the last guard
// before reclaiming bytes that a fork copy, a legacy raw path or a later
// re-attachment may still need.
CountLiveArtifactsByURL(ctx context.Context, url string) (int64, error)
// GetSessionAttachments returns every user-uploaded attachment recorded in
// the session. Implementations should project only the attachments column.
GetSessionAttachments(ctx context.Context, sessionID string) (types.MessageAttachments, error)
// RewriteSandboxCheckpoints replaces SandboxID on every copied checkpoint
// in the session that still points at oldSandboxID, keeping CommitSHA and
// CommittedAt. Used after a forked sandbox boots so a later fork-of-fork
// compares against the live handle rather than the parent's sandbox.
RewriteSandboxCheckpoints(ctx context.Context, sessionID, oldSandboxID, newSandboxID string) error
}
MessageRepository defines the message repository interface
type MessageService ¶
type MessageService interface {
// CreateMessage creates a message
CreateMessage(ctx context.Context, message *types.Message) (*types.Message, error)
// GetMessage gets a message
GetMessage(ctx context.Context, sessionID string, id string) (*types.Message, error)
// GetMessagesBySession gets all messages of a session
GetMessagesBySession(ctx context.Context, sessionID string, page int, pageSize int) ([]*types.Message, error)
// GetRecentMessagesBySession gets recent messages of a session
GetRecentMessagesBySession(ctx context.Context, sessionID string, limit int) ([]*types.Message, error)
// GetMessagesBySessionBeforeTime gets messages before a specific time of a session
GetMessagesBySessionBeforeTime(
ctx context.Context, sessionID string, beforeTime time.Time, limit int,
) ([]*types.Message, error)
// UpdateMessage updates a message
UpdateMessage(ctx context.Context, message *types.Message) error
// UpdateMessageImages updates only the images JSONB column for a message.
UpdateMessageImages(ctx context.Context, sessionID, messageID string, images types.MessageImages) error
// UpdateMessageRenderedContent updates the rendered_content column for a user message.
UpdateMessageRenderedContent(ctx context.Context, sessionID, messageID string, renderedContent string) error
// DeleteMessage deletes a message
DeleteMessage(ctx context.Context, sessionID string, id string) error
// ClearSessionMessages deletes all messages in a session, along with their chat history KB entries
ClearSessionMessages(ctx context.Context, sessionID string) error
// SearchMessages searches messages by keyword and/or vector similarity across the caller's own sessions.
// Uses the chat history knowledge base for vector search instead of in-memory computation.
SearchMessages(ctx context.Context, params *types.MessageSearchParams) (*types.MessageSearchResult, error)
// IndexMessageToKB indexes a message (Q&A pair) into the chat history knowledge base asynchronously.
// Called after assistant message is created to enable future vector search.
IndexMessageToKB(ctx context.Context, userQuery string, assistantAnswer string, messageID string, sessionID string)
// DeleteMessageKnowledge deletes the Knowledge entry associated with a message from the chat history KB.
DeleteMessageKnowledge(ctx context.Context, knowledgeID string)
// DeleteSessionKnowledge deletes all Knowledge entries for messages in a session from the chat history KB.
DeleteSessionKnowledge(ctx context.Context, sessionID string)
// GetChatHistoryKBStats returns statistics about the chat history knowledge base (indexed message count, etc.)
GetChatHistoryKBStats(ctx context.Context) (*types.ChatHistoryKBStats, error)
// GetSessionArtifacts returns every skill-produced MessageArtifact
// recorded against any assistant message of the session. Used to power
// the frontend "download files generated in this session" drawer and
// to clean up storage blobs on session deletion.
GetSessionArtifacts(ctx context.Context, sessionID string) (types.MessageArtifacts, error)
// ListArtifactLibrary lists the latest version of every artifact across
// the caller's sessions, newest first. Tenant and owner scope come from
// ctx; query.TenantID and query.UserID are overwritten.
ListArtifactLibrary(ctx context.Context, query *types.ArtifactLibraryQuery) (*types.PageResult, error)
// DeleteSessionArtifact tombstones a skill-generated file (and optionally
// every version of it) in a session the caller owns, and reports the blobs
// whose bytes the caller should now reclaim. Shared-agent read access does
// not carry delete rights.
DeleteSessionArtifact(
ctx context.Context, req *types.ArtifactDeleteRequest,
) (*types.ArtifactDeleteResult, error)
}
MessageService defines the message service interface
type MessageSuggestionRepository ¶
type MessageSuggestionRepository interface {
GetByCacheKey(
ctx context.Context,
tenantID uint64,
assistantMessageID string,
placement string,
configHash string,
locale string,
) (*types.MessageSuggestionSet, error)
GetByID(ctx context.Context, tenantID uint64, sessionID string, id string) (*types.MessageSuggestionSet, error)
AcquireGeneration(
ctx context.Context,
set *types.MessageSuggestionSet,
regenerate bool,
) (*types.MessageSuggestionSet, bool, error)
Save(ctx context.Context, set *types.MessageSuggestionSet) error
CreateEvent(ctx context.Context, event *types.MessageSuggestionEvent) error
DeleteByMessageID(ctx context.Context, tenantID uint64, sessionID string, messageID string) error
DeleteBySessionID(ctx context.Context, tenantID uint64, sessionID string) error
}
MessageSuggestionRepository persists generated suggestions and their product-analytics events. AcquireGeneration serializes duplicate requests from reconnecting clients with an expiring lease.
type MessageSuggestionService ¶
type MessageSuggestionService interface {
EnsureFollowUps(
ctx context.Context,
sessionID string,
assistantMessageID string,
regenerate bool,
) (*types.MessageSuggestionSet, error)
GetFollowUps(
ctx context.Context,
sessionID string,
assistantMessageID string,
) (*types.MessageSuggestionSet, error)
RecordEvent(
ctx context.Context,
sessionID string,
setID string,
questionID string,
eventType string,
) error
ValidateAttribution(ctx context.Context, sessionID string, query string, attribution *types.SuggestionAttribution) error
}
type Metrics ¶
type Metrics interface {
// Compute calculates metric score based on input data
Compute(metricInput *types.MetricInput) float64
}
Metrics defines interface for computing evaluation metrics
type ModelRepository ¶
type ModelRepository interface {
// Create creates a model
Create(ctx context.Context, model *types.Model) error
// GetByID gets a model by ID
GetByID(ctx context.Context, tenantID uint64, id string) (*types.Model, error)
// List lists all models
List(
ctx context.Context,
tenantID uint64,
modelType types.ModelType,
source types.ModelSource,
) ([]*types.Model, error)
// Update updates a model
Update(ctx context.Context, model *types.Model) error
// Delete deletes a model
Delete(ctx context.Context, tenantID uint64, id string) error
// ClearDefaultByType clears the default flag for all models of a specific type
// optionally excluding a specific model ID.
ClearDefaultByType(ctx context.Context, tenantID uint, modelType types.ModelType, excludeID string) error
}
ModelRepository defines the model repository interface
type ModelService ¶
type ModelService interface {
// CreateModel creates a model
CreateModel(ctx context.Context, model *types.Model) error
// CopyModel clones a tenant model. The model name stays the source
// identifier; displayName is the caller-chosen label. Stored credentials
// are copied with the row. An active local model is not downloaded again.
CopyModel(ctx context.Context, sourceID, displayName string) (*types.Model, error)
// GetModelByID gets a model by ID
GetModelByID(ctx context.Context, id string) (*types.Model, error)
// ListModels lists all models
ListModels(ctx context.Context) ([]*types.Model, error)
// UpdateModel updates a model
UpdateModel(ctx context.Context, model *types.Model) error
// DeleteModel deletes a model
DeleteModel(ctx context.Context, id string) error
// UpdateModelCredentials writes one or more credential fields on the
// model's Parameters. Nil pointer means "do not touch this field";
// empty string is treated as no-op (use ClearModelCredential to remove).
// Returns the updated model.
UpdateModelCredentials(ctx context.Context, id string, apiKey, appSecret *string) (*types.Model, error)
// ClearModelCredential removes a single credential field. field must be
// "api_key" or "app_secret". Clearing an already-empty field is a no-op.
ClearModelCredential(ctx context.Context, id, field string) error
// GetEmbeddingModel gets an embedding model
GetEmbeddingModel(ctx context.Context, modelId string) (embedding.Embedder, error)
// GetEmbeddingModelForTenant gets an embedding model for a specific tenant (for cross-tenant sharing)
GetEmbeddingModelForTenant(ctx context.Context, modelId string, tenantID uint64) (embedding.Embedder, error)
// GetRerankModel gets a rerank model
GetRerankModel(ctx context.Context, modelId string) (rerank.Reranker, error)
// GetChatModel gets a chat model
GetChatModel(ctx context.Context, modelId string) (chat.Chat, error)
// GetVLMModel gets a vision language model
GetVLMModel(ctx context.Context, modelId string) (vlm.VLM, error)
// GetASRModel gets an automatic speech recognition model
GetASRModel(ctx context.Context, modelId string) (asr.ASR, error)
}
ModelService defines the model service interface
type OrganizationRepository ¶
type OrganizationRepository interface {
// Organization CRUD
Create(ctx context.Context, org *types.Organization) error
GetByID(ctx context.Context, id string) (*types.Organization, error)
GetByInviteCode(ctx context.Context, inviteCode string) (*types.Organization, error)
ListByTenantID(ctx context.Context, tenantID uint64) ([]*types.Organization, error)
ListSearchable(ctx context.Context, query string, limit int) ([]*types.Organization, error)
Update(ctx context.Context, org *types.Organization) error
Delete(ctx context.Context, id string) error
// Tenant member operations
// AddTenantMember inserts the membership; a positive memberLimit is enforced
// atomically with the insert.
AddTenantMember(ctx context.Context, member *types.OrganizationTenantMember, memberLimit int) error
RemoveTenantMember(ctx context.Context, orgID string, tenantID uint64) error
UpdateTenantMemberRole(ctx context.Context, orgID string, tenantID uint64, role types.OrgMemberRole) error
ListTenantMembers(ctx context.Context, orgID string) ([]*types.OrganizationTenantMember, error)
GetTenantMember(ctx context.Context, orgID string, tenantID uint64) (*types.OrganizationTenantMember, error)
ListTenantMembersByTenantForOrgs(ctx context.Context, tenantID uint64, orgIDs []string) (map[string]*types.OrganizationTenantMember, error)
CountTenantMembers(ctx context.Context, orgID string) (int64, error)
// Invite code
UpdateInviteCode(ctx context.Context, orgID string, inviteCode string, expiresAt *time.Time) error
// Join requests (still keyed on the *requesting* user, but a single
// pending request per (org, tenant) since the approve action grants
// the whole tenant access).
CreateJoinRequest(ctx context.Context, request *types.OrganizationJoinRequest) error
GetJoinRequestByID(ctx context.Context, id string) (*types.OrganizationJoinRequest, error)
GetPendingJoinRequestByTenant(ctx context.Context, orgID string, tenantID uint64) (*types.OrganizationJoinRequest, error)
GetPendingRequestByTenantAndType(ctx context.Context, orgID string, tenantID uint64, requestType types.JoinRequestType) (*types.OrganizationJoinRequest, error)
ListJoinRequests(ctx context.Context, orgID string, status types.JoinRequestStatus) ([]*types.OrganizationJoinRequest, error)
CountJoinRequests(ctx context.Context, orgID string, status types.JoinRequestStatus) (int64, error)
UpdateJoinRequestStatus(ctx context.Context, id string, status types.JoinRequestStatus, reviewedBy string, reviewMessage string) error
}
OrganizationRepository defines the organization repository interface.
Members are stored per-tenant in `organization_tenant_members`.
type OrganizationService ¶
type OrganizationService interface {
// Organization CRUD
CreateOrganization(ctx context.Context, userID string, tenantID uint64, req *types.CreateOrganizationRequest) (*types.Organization, error)
GetOrganization(ctx context.Context, id string) (*types.Organization, error)
GetOrganizationByInviteCode(ctx context.Context, inviteCode string) (*types.Organization, error)
ListTenantOrganizations(ctx context.Context, tenantID uint64) ([]*types.Organization, error)
UpdateOrganization(ctx context.Context, id string, userID string, tenantID uint64, req *types.UpdateOrganizationRequest) (*types.Organization, error)
DeleteOrganization(ctx context.Context, id string, userID string, tenantID uint64) error
// Member Management (Plan 3: members are tenants)
// AddTenantMember adds a tenant to the org with the given role; representativeUserID
// is only used for display labelling.
AddTenantMember(ctx context.Context, orgID string, tenantID uint64, representativeUserID string, role types.OrgMemberRole) error
RemoveTenantMember(ctx context.Context, orgID string, memberTenantID uint64, operatorUserID string, operatorTenantID uint64) error
UpdateTenantMemberRole(ctx context.Context, orgID string, memberTenantID uint64, role types.OrgMemberRole, operatorUserID string, operatorTenantID uint64) error
ListTenantMembers(ctx context.Context, orgID string) ([]*types.OrganizationTenantMember, error)
GetTenantMember(ctx context.Context, orgID string, tenantID uint64) (*types.OrganizationTenantMember, error)
// Invite Code
GenerateInviteCode(ctx context.Context, orgID string, userID string, tenantID uint64) (string, error)
JoinByInviteCode(ctx context.Context, inviteCode string, userID string, tenantID uint64) (*types.Organization, error)
// Searchable organizations (discovery)
SearchSearchableOrganizations(ctx context.Context, tenantID uint64, query string, limit int) (*types.ListSearchableOrganizationsResponse, error)
JoinByOrganizationID(ctx context.Context, orgID string, userID string, tenantID uint64, message string, requestedRole types.OrgMemberRole) (*types.Organization, error)
// Join Requests (for organizations that require approval)
SubmitJoinRequest(ctx context.Context, orgID string, userID string, tenantID uint64, message string, requestedRole types.OrgMemberRole) (*types.OrganizationJoinRequest, error)
ListJoinRequests(ctx context.Context, orgID string) ([]*types.OrganizationJoinRequest, error)
CountPendingJoinRequests(ctx context.Context, orgID string) (int64, error)
ReviewJoinRequest(ctx context.Context, orgID string, requestID string, approved bool, reviewerID string, reviewerTenantID uint64, message string, assignRole *types.OrgMemberRole) error
// Role Upgrade Requests (for existing tenants to request higher permissions)
RequestRoleUpgrade(ctx context.Context, orgID string, userID string, tenantID uint64, requestedRole types.OrgMemberRole, message string) (*types.OrganizationJoinRequest, error)
GetPendingUpgradeRequest(ctx context.Context, orgID string, tenantID uint64) (*types.OrganizationJoinRequest, error)
// Permission Check (drive purely off the requesting tenant)
IsTenantOrgAdmin(ctx context.Context, orgID string, tenantID uint64) (bool, error)
GetTenantRoleInOrg(ctx context.Context, orgID string, tenantID uint64) (types.OrgMemberRole, error)
}
OrganizationService defines the organization service interface.
Plan 3 of #1303: organization membership is per-tenant, not per-user. Adding/removing/updating "members" therefore takes a tenant_id, and permission checks key on (org, tenant). RepresentativeUserID is informational and not load-bearing.
type ResourceCatalog ¶
type ResourceCatalog interface {
Register(ctx context.Context, tenantID uint64, physicalPath string, meta ResourceRegistration) (string, error)
Resolve(ctx context.Context, reference string) (*types.StoredResource, error)
ResolvePath(ctx context.Context, value string) (string, *types.StoredResource, error)
Bind(ctx context.Context, reference, ownerType, ownerID, relation string) error
// Release drops one owner's claim on a resource and reports how many
// claims remain. Callers use the count to decide whether the bytes may be
// deleted: a resource still referenced by another owner — the assistant
// message an answer was saved from, a second knowledge entry — must
// survive the owner being removed.
//
// remaining is -1 when the answer is unknown (the reference is not a
// catalog handle, or the count could not be read), which callers should
// treat as "delete as before" rather than as "keep forever".
Release(ctx context.Context, reference, ownerType, ownerID string) (remaining int64, err error)
// ListReferencesByOwner returns derived resource:// handles still claimed
// by any of the given owners (extracted images and markdown attachments).
// Source files are omitted: reparse/cleanup must keep the original
// document, which knowledge delete removes through FilePath.
//
// Knowledge-delete paths union this with ImageInfo URLs so markdown-only
// images are released even when multimodal never wrote ImageInfo. Cleanup
// before a re-index must not: it leaves the knowledge in place, and the
// handles it released could not be re-bound once DeleteFile marked them
// deleted.
ListReferencesByOwner(ctx context.Context, ownerType string, ownerIDs ...string) ([]string, error)
MarkDeleted(ctx context.Context, reference string) error
CreateAccessGrant(ctx context.Context, reference string, ttl time.Duration) (string, error)
ResolveAccessGrant(ctx context.Context, token string) (*types.StoredResource, error)
}
ResourceCatalog maps public resource references to internal storage locations and manages their access capabilities.
type ResourceCleaner ¶
type ResourceCleaner interface {
Register(cleanup types.CleanupFunc)
RegisterWithName(name string, cleanup types.CleanupFunc)
// Promote moves callbacks registered under name so Cleanup runs them
// first. Cleanup's order is reverse registration order.
Promote(name string)
Cleanup(ctx context.Context) []error
}
ResourceCleaner owns process-lifetime cleanup callbacks (database clients, worker pools, etc.). It is unrelated to persisted StoredResource records.
type ResourceRegistration ¶
type ResourceRegistration struct {
Kind string
MimeType string
OriginalName string
Size int64
ContentHash string
Temporary bool
}
ResourceRegistration describes one physical object at registration time.
type ResourceRepository ¶
type ResourceRepository interface {
Create(ctx context.Context, resource *types.StoredResource) error
GetByID(ctx context.Context, id string) (*types.StoredResource, error)
GetByHandle(ctx context.Context, handle string) (*types.StoredResource, error)
GetByTenantLocation(ctx context.Context, tenantID uint64, locationHash string) (*types.StoredResource, error)
MarkDeleted(ctx context.Context, id string) error
CreateBinding(ctx context.Context, binding *types.ResourceBinding) error
DeleteBinding(ctx context.Context, resourceID, ownerType, ownerID string) error
CountBindings(ctx context.Context, resourceID string) (int64, error)
ListHandlesByOwner(ctx context.Context, ownerType string, ownerIDs, relations []string) ([]string, error)
IsReferencedByKnowledgeBase(
ctx context.Context,
tenantID uint64,
kbID, resourceID string,
) (bool, error)
GetMessageFileBindings(
ctx context.Context,
tenantID uint64,
resourceID, messageID string,
) (*types.MessageFileBindings, error)
CreateGrant(ctx context.Context, grant *types.ResourceAccessGrant) error
GetValidGrant(ctx context.Context, tokenHash string, now time.Time) (*types.ResourceAccessGrant, error)
DeleteExpiredGrants(ctx context.Context, before time.Time) error
}
ResourceRepository persists stable resource identities, ownership bindings, and short-lived access grants.
type RetrievalContext ¶
type RetrievalContext struct {
// Background is a compact description of the person, for query rewriting.
Background string
// Interests are the topics they keep returning to.
Interests []string
// Documents are titles they rely on, as vocabulary for the rewriter.
Documents []string
// Items are the memories behind the above, so the UI can show what
// influenced retrieval instead of it happening invisibly.
Items []*types.MemoryItem
}
RetrievalContext is what memory contributes to retrieval rather than to the answer prompt: who this person is, what they keep asking about, and which documents they rely on.
This is the part of memory that earns its keep in a knowledge-base product. The same question means different things to different people — "how do I tune the segmentation" from someone working on medical imaging should not retrieve the same passages as it would from someone working on autonomous driving — and that difference has to be applied before retrieval, not after.
func (RetrievalContext) Empty ¶
func (c RetrievalContext) Empty() bool
type RetrieveEngine ¶
type RetrieveEngine interface {
// EngineType gets the retrieve engine type
EngineType() types.RetrieverEngineType
// Retrieve executes the retrieve
Retrieve(ctx context.Context, params types.RetrieveParams) ([]*types.RetrieveResult, error)
// Support gets the supported retrieve types
Support() []types.RetrieverType
}
RetrieveEngine defines the retrieve engine interface
type RetrieveEngineRegistry ¶
type RetrieveEngineRegistry interface {
// Register registers the retrieve engine service
Register(indexService RetrieveEngineService) error
// GetRetrieveEngineService gets the retrieve engine service
GetRetrieveEngineService(engineType types.RetrieverEngineType) (RetrieveEngineService, error)
// GetAllRetrieveEngineServices gets all retrieve engine services
GetAllRetrieveEngineServices() []RetrieveEngineService
// GetByStoreID returns the engine service registered for a specific DB store ID.
//
// IMPORTANT: This method does NOT verify tenant ownership of the returned
// store. Callers MUST use the CreateRetrieveEngineForKB /
// CreateRetrieveEngineFromPayload factory functions in the retriever package
// rather than calling this directly. The factories wrap GetByStoreID with
// tenant ownership verification (defense-in-depth against cross-tenant IDOR).
GetByStoreID(storeID string) (RetrieveEngineService, error)
// GetOrLoadByStoreID returns the engine for storeID, rebuilding it from the
// database when this process has no entry for it. The registry is per-process:
// an engine registered on one instance is missing on every other until that
// instance restarts, and an engine whose creation failed during startup stays
// missing even across restarts. Rebuilding on demand lets both cases recover
// without an operator-driven rollout.
//
// Unlike GetByStoreID, this method scopes its database lookup to tenantID, so
// it cannot hydrate a store belonging to another tenant. Callers should still
// verify ownership first: the tenant scope is defense-in-depth, not a
// replacement for the ownership check.
GetOrLoadByStoreID(
ctx context.Context, tenantID uint64, storeID string,
) (RetrieveEngineService, error)
}
RetrieveEngineRegistry defines the retrieve engine registry interface
type RetrieveEngineRepository ¶
type RetrieveEngineRepository interface {
// Save saves the index info
Save(ctx context.Context, indexInfo *types.IndexInfo, params map[string]any) error
// BatchSave saves the index info list
BatchSave(ctx context.Context, indexInfoList []*types.IndexInfo, params map[string]any) error
// EstimateStorageSize estimates the storage size
EstimateStorageSize(ctx context.Context, indexInfoList []*types.IndexInfo, params map[string]any) int64
// DeleteByChunkIDList deletes the index info by chunk id list
DeleteByChunkIDList(ctx context.Context, indexIDList []string, dimension int, knowledgeType string) error
// DeleteBySourceIDList deletes the index info by source id list
DeleteBySourceIDList(ctx context.Context, sourceIDList []string, dimension int, knowledgeType string) error
// 复制索引数据
// sourceKnowledgeBaseID: 源知识库ID
// sourceToTargetChunkIDMap: 源分块ID到目标分块ID的映射关系
// targetKnowledgeBaseID: 目标知识库ID
// params: 额外参数,如向量表示等
CopyIndices(
ctx context.Context,
sourceKnowledgeBaseID string,
sourceToTargetKBIDMap map[string]string,
sourceToTargetChunkIDMap map[string]string,
targetKnowledgeBaseID string,
dimension int,
knowledgeType string,
) error
// DeleteByKnowledgeIDList deletes the index info by knowledge id list
DeleteByKnowledgeIDList(ctx context.Context, knowledgeIDList []string, dimension int, knowledgeType string) error
// BatchUpdateChunkEnabledStatus updates the enabled status of chunks in batch
// chunkStatusMap: map of chunk ID to enabled status (true = enabled, false = disabled)
BatchUpdateChunkEnabledStatus(ctx context.Context, chunkStatusMap map[string]bool) error
// BatchUpdateChunkTagID updates the tag ID of chunks in batch
// chunkTagMap: map of chunk ID to tag ID (empty string means no tag)
BatchUpdateChunkTagID(ctx context.Context, chunkTagMap map[string]string) error
// RetrieveEngine retrieves the engine
RetrieveEngine
}
RetrieveEngineRepository defines the retrieve engine repository interface
type RetrieveEngineService ¶
type RetrieveEngineService interface {
// Index indexes the index info
Index(ctx context.Context,
embedder embedding.Embedder,
indexInfo *types.IndexInfo,
retrieverTypes []types.RetrieverType,
) error
// BatchIndex indexes the index info list
BatchIndex(ctx context.Context,
embedder embedding.Embedder,
indexInfoList []*types.IndexInfo,
retrieverTypes []types.RetrieverType,
) error
// EstimateStorageSize estimates the storage size
EstimateStorageSize(ctx context.Context,
embedder embedding.Embedder,
indexInfoList []*types.IndexInfo,
retrieverTypes []types.RetrieverType,
) int64
// CopyIndices 从源知识库复制索引到目标知识库,免去重新计算嵌入向量的开销
// sourceKnowledgeBaseID: 源知识库ID
// sourceToTargetChunkIDMap: 源分块ID到目标分块ID的映射关系,key为源分块ID,value为目标分块ID
// targetKnowledgeBaseID: 目标知识库ID
CopyIndices(
ctx context.Context,
sourceKnowledgeBaseID string,
sourceToTargetKBIDMap map[string]string,
sourceToTargetChunkIDMap map[string]string,
targetKnowledgeBaseID string,
dimension int,
knowledgeType string,
) error
// DeleteByChunkIDList deletes the index info by chunk id list
DeleteByChunkIDList(ctx context.Context, indexIDList []string, dimension int, knowledgeType string) error
// DeleteBySourceIDList deletes the index info by source id list
DeleteBySourceIDList(ctx context.Context, sourceIDList []string, dimension int, knowledgeType string) error
// DeleteByKnowledgeIDList deletes the index info by knowledge id list
DeleteByKnowledgeIDList(ctx context.Context, knowledgeIDList []string, dimension int, knowledgeType string) error
// BatchUpdateChunkEnabledStatus updates the enabled status of chunks in batch
// chunkStatusMap: map of chunk ID to enabled status (true = enabled, false = disabled)
BatchUpdateChunkEnabledStatus(ctx context.Context, chunkStatusMap map[string]bool) error
// BatchUpdateChunkTagID updates the tag ID of chunks in batch
// chunkTagMap: map of chunk ID to tag ID (empty string means no tag)
BatchUpdateChunkTagID(ctx context.Context, chunkTagMap map[string]string) error
// RetrieveEngine retrieves the engine
RetrieveEngine
}
RetrieveEngineService defines the retrieve engine service interface
type RetrieveGraphRepository ¶
type RetrieveGraphRepository interface {
// AddGraph adds a graph to the repository
AddGraph(ctx context.Context, namespace types.NameSpace, graphs []*types.GraphData) error
// DelGraph deletes a graph from the repository
DelGraph(ctx context.Context, namespace []types.NameSpace) error
// SearchNode searches for nodes in the repository
SearchNode(ctx context.Context, namespace types.NameSpace, nodes []string) (*types.GraphData, error)
}
RetrieveGraphRepository is a repository for retrieving graphs
type RuntimeTaskInspector ¶
type RuntimeTaskInspector interface {
ListRuntimeTasks(
ctx context.Context,
queue string,
state types.RuntimeTaskState,
cursor string,
pageSize int,
) (page types.RuntimeTaskPage, supported bool, err error)
GetRuntimeTask(ctx context.Context, queue, taskID string) (task *types.RuntimeTaskInfo, supported bool, err error)
RunRuntimeTask(ctx context.Context, queue, taskID string) (supported bool, err error)
DeleteRuntimeTask(ctx context.Context, queue, taskID string) (supported bool, err error)
// ForceDeleteRuntimeTask removes a queue record without checking the
// operator-facing AllowedActions. Used when the business row is already
// gone but a retry/pending task survived (orphan cleanup).
ForceDeleteRuntimeTask(ctx context.Context, queue, taskID string) (supported bool, err error)
// PurgeArchivedRuntimeTasks removes every archived (finally-failed) task
// from one queue in a single call and returns how many records were
// deleted. It only ever touches the archived (dead-letter) set, so live
// pending/active/scheduled/retry tasks are never affected. Business state
// is intentionally left untouched: archived tasks have already had their
// document status flipped to "failed" on their last retry, mirroring the
// semantics of deleting archived records one by one.
PurgeArchivedRuntimeTasks(ctx context.Context, queue string) (deleted int, supported bool, err error)
}
RuntimeTaskInspector is the optional operator surface implemented by queue backends that retain inspectable task state. It is separate from TaskInspector so Lite mode and light-weight tests do not need to implement queue mutations.
type SessionRepository ¶
type SessionRepository interface {
// Create creates a session
Create(ctx context.Context, session *types.Session) (*types.Session, error)
// Get gets a session visible to the tenant/user scope.
Get(ctx context.Context, tenantID uint64, userID string, id string) (*types.Session, error)
// GetByID loads a session by tenant and id without user scoping. Callers
// must enforce access (e.g. embed channel + session signature).
GetByID(ctx context.Context, tenantID uint64, id string) (*types.Session, error)
// GetIMPlatform returns the IM platform bound to a session via
// im_channel_sessions, or "" when the session has no IM mapping. Used to
// classify a session's origin folder on read without a full list query.
GetIMPlatform(ctx context.Context, tenantID uint64, sessionID string) (string, error)
// GetByTenantID gets all sessions visible to the tenant/user scope.
GetByTenantID(ctx context.Context, tenantID uint64, userID string) ([]*types.Session, error)
// GetPagedByTenantID gets paged sessions visible to the tenant/user scope.
GetPagedByTenantID(ctx context.Context, tenantID uint64, userID string, page *types.Pagination) ([]*types.Session, int64, error)
// QueryPaged lists sessions with filters, user-scoped ownership and pin-aware ordering.
QueryPaged(ctx context.Context, q *types.SessionListQuery) ([]*types.SessionListItem, int64, error)
// Update updates a session visible to the tenant/user scope.
Update(ctx context.Context, session *types.Session, userID string) (int64, error)
// SetOwnerID assigns sessions.user_id for a tenant-scoped row.
SetOwnerID(ctx context.Context, tenantID uint64, id, ownerID string) (int64, error)
// UpdateLastRequestState persists the most recent input-bar state for a
// session (agent, model, KB scope, etc.) so the chat UI can restore it
// when the session is reopened. Scope rules match Update.
UpdateLastRequestState(ctx context.Context, tenantID uint64, userID string, sessionID string, state *types.SessionLastRequestState) (int64, error)
// SetPinned pins or unpins a session row scoped by tenant.
// userID, when non-empty, is enforced so users cannot pin sessions they don't own.
// Returns the number of rows affected; 0 means the session doesn't exist or is
// not visible to this caller.
SetPinned(ctx context.Context, tenantID uint64, userID string, id string, pinned bool) (int64, error)
// Delete deletes a session visible to the tenant/user scope.
Delete(ctx context.Context, tenantID uint64, userID string, id string) (int64, error)
// BatchDelete deletes multiple sessions visible to the tenant/user scope.
BatchDelete(ctx context.Context, tenantID uint64, userID string, ids []string) (int64, error)
// DeleteAllByTenantID deletes all sessions visible to the tenant/user scope.
DeleteAllByTenantID(ctx context.Context, tenantID uint64, userID string) (int64, error)
// CreateForked persists a forked session and its copied history atomically.
CreateForked(ctx context.Context, session *types.Session, messages []*types.Message) error
// UpdateForkBootstrap overwrites a session's fork bootstrap. Passing nil clears it.
UpdateForkBootstrap(ctx context.Context, sessionID string, b *types.ForkBootstrap) error
// ListUnconsumedForks returns fork bootstraps the snapshot reaper should
// try to retire: unopened forks older than olderThan, plus consumed forks
// that still name a snapshot.
ListUnconsumedForks(ctx context.Context, olderThan time.Time) ([]*types.Session, error)
// HasOtherUnconsumedForkSnapshot reports whether another session still
// needs snapshotID to boot. excludeSessionID is the row currently being
// consumed or reaped.
HasOtherUnconsumedForkSnapshot(ctx context.Context, snapshotID, excludeSessionID string) (bool, error)
// UnconsumedForkSnapshotHolders returns session IDs that still need
// snapshotID to provision. The reaper uses this to avoid deleting a
// snapshot while a fork within retention still depends on it.
UnconsumedForkSnapshotHolders(ctx context.Context, snapshotID string) ([]string, error)
// CreateForkSnapshotLease records a provider snapshot ID before the forked
// session row exists, so a crash or CreateForked failure cannot hide it
// from the reaper.
CreateForkSnapshotLease(ctx context.Context, lease *types.ForkSnapshotLease) error
// DeleteForkSnapshotLease drops a lease after the session owns the
// snapshot, or after the snapshot itself has been deleted.
DeleteForkSnapshotLease(ctx context.Context, snapshotID string) error
// ListStaleForkSnapshotLeases returns leases older than olderThan.
ListStaleForkSnapshotLeases(ctx context.Context, olderThan time.Time) ([]*types.ForkSnapshotLease, error)
}
SessionRepository defines the session repository interface
type SessionService ¶
type SessionService interface {
// CreateSession creates a session
CreateSession(ctx context.Context, session *types.Session) (*types.Session, error)
// GetSession gets a session, honoring the caller's per-user scope with an
// Admin+ read fallback for tenant channel sessions (API / IM / embed).
// Use only for read paths.
GetSession(ctx context.Context, id string) (*types.Session, error)
// GetOwnedSession gets a session strictly within the caller's owner scope
// (no Admin+ API-key fallback). Write/mutation endpoints must use this so a
// tenant admin cannot alter API-key sessions they can only read.
GetOwnedSession(ctx context.Context, id string) (*types.Session, error)
// GetSessionByID loads a session by tenant and id without user scoping.
GetSessionByID(ctx context.Context, tenantID uint64, id string) (*types.Session, error)
// SetSessionOwnerID assigns sessions.user_id for the given session row.
SetSessionOwnerID(ctx context.Context, tenantID uint64, sessionID, ownerID string) error
// GetSessionsByTenant gets all sessions of a tenant
GetSessionsByTenant(ctx context.Context) ([]*types.Session, error)
// GetPagedSessionsByTenant gets paged sessions of a tenant
GetPagedSessionsByTenant(ctx context.Context, page *types.Pagination) (*types.PageResult, error)
// UpdateSession updates a session
UpdateSession(ctx context.Context, session *types.Session) error
// UpdateSessionLastRequestState records the input-bar state used for the
// most recent QA request on this session. Best-effort: callers should log
// but not surface failures to the user.
UpdateSessionLastRequestState(ctx context.Context, sessionID string, state *types.SessionLastRequestState) error
// DeleteSession deletes a session
DeleteSession(ctx context.Context, id string) error
// BatchDeleteSessions deletes multiple sessions by IDs
BatchDeleteSessions(ctx context.Context, ids []string) error
// DeleteAllSessions deletes all sessions for the current tenant
DeleteAllSessions(ctx context.Context) error
// ListSessions returns a page of sessions for the current tenant/user with
// search/source filters and pin-aware ordering. User scope is pulled from ctx.
ListSessions(ctx context.Context, query *types.SessionListQuery) (*types.PageResult, error)
// CountSessionsBySource returns the total for a source filter without the
// Admin+ gate applied by ListSessions (for aggregate stats endpoints).
CountSessionsBySource(ctx context.Context, query *types.SessionListQuery) (int64, error)
// SetSessionPinned pins or unpins the session for the current user scope.
// Returns the number of rows affected; 0 signals "not found" to the handler.
SetSessionPinned(ctx context.Context, sessionID string, pinned bool) (int64, error)
// GenerateTitle generates a title for the current conversation
// modelID: optional model ID to use for title generation (if empty, uses first available KnowledgeQA model)
GenerateTitle(ctx context.Context, session *types.Session, messages []types.Message, modelID string) (string, error)
// GenerateTitleAsync generates a title for the session asynchronously
// It emits an event when the title is generated
// modelID: optional model ID to use for title generation (if empty, uses first available KnowledgeQA model)
GenerateTitleAsync(ctx context.Context, session *types.Session, userQuery string, modelID string, eventBus *event.EventBus)
// KnowledgeQA performs knowledge-based question answering.
// Events are emitted through eventBus (references, answer chunks, completion).
KnowledgeQA(ctx context.Context, req *types.QARequest, eventBus *event.EventBus) error
// KnowledgeQAByEvent performs knowledge-based question answering by event
KnowledgeQAByEvent(ctx context.Context, chatManage *types.ChatManage, eventList []types.EventType) error
// SearchKnowledge performs knowledge-based search, without summarization
// knowledgeBaseIDs: list of knowledge base IDs to search (supports multi-KB)
// knowledgeIDs: list of specific knowledge (file) IDs to search
// opts: caller overrides of the tenant retrieval config; nil keeps it
SearchKnowledge(
ctx context.Context,
knowledgeBaseIDs []string,
knowledgeIDs []string,
tagScopes []types.TagScope,
query string,
opts *types.KnowledgeSearchOptions,
) (*types.RetrievalResult, error)
// AgentQA performs agent-based question answering with conversation history and streaming support.
AgentQA(ctx context.Context, req *types.QARequest, eventBus *event.EventBus) error
}
SessionService defines the session service interface
type StorageBackendRepository ¶
type StorageBackendRepository interface {
Create(ctx context.Context, backend *types.StorageBackend) error
GetByID(ctx context.Context, tenantID uint64, id string) (*types.StorageBackend, error)
List(ctx context.Context, tenantID uint64) ([]*types.StorageBackend, error)
Update(ctx context.Context, backend *types.StorageBackend) error
Delete(ctx context.Context, tenantID uint64, id string) error
FindLegacyAlias(ctx context.Context, tenantID uint64, provider string) (*types.StorageBackend, error)
}
type StorageBackendResolver ¶
type StorageBackendResolver interface {
ResolveFileService(ctx context.Context, tenant *types.Tenant, backendID, provider, localBaseDir string) (FileService, string, error)
ResolveBackend(ctx context.Context, tenant *types.Tenant, backendID, provider string) (*types.StorageBackend, error)
}
StorageBackendResolver is the single runtime entry point for resolving one concrete storage instance. backendID wins; provider is a legacy fallback.
type StorageBackendService ¶
type StorageBackendService interface {
Create(ctx context.Context, backend *types.StorageBackend) error
Update(ctx context.Context, backend *types.StorageBackend) error
Delete(ctx context.Context, tenantID uint64, id string) error
SetDefault(ctx context.Context, tenantID uint64, id string) error
Test(ctx context.Context, backend *types.StorageBackend) error
}
type StoreRegistry ¶
type StoreRegistry interface {
// RegisterWithStoreID registers an engine service by VectorStore ID.
// Upsert semantics: existing entry is overwritten silently.
RegisterWithStoreID(storeID string, svc RetrieveEngineService)
// GetByStoreID retrieves an engine service by VectorStore ID.
GetByStoreID(storeID string) (RetrieveEngineService, error)
// UnregisterByStoreID removes an engine service by VectorStore ID (idempotent).
UnregisterByStoreID(storeID string)
}
StoreRegistry provides VectorStore-based engine registration/lookup. Separated from RetrieveEngineRegistry to avoid changing the existing interface used by 6 services (17 call sites). Phase 2 may merge into RetrieveEngineRegistry.
type StreamEvent ¶
type StreamEvent struct {
ID string `json:"id"` // Unique event ID
Type types.ResponseType `json:"type"` // Event type (thinking, tool_call, complete, etc.)
Content string `json:"content"` // Event content (chunk for streaming events)
Done bool `json:"done"` // Whether this event is done
Timestamp time.Time `json:"timestamp"` // When this event occurred
Data map[string]interface{} `json:"data,omitempty"` // Additional event data (references, metadata, etc.)
Usage *types.TokenUsage `json:"usage,omitempty"` // LLM token usage aggregated over the turn (complete events)
}
StreamEvent represents a single event in the stream
type StreamManager ¶
type StreamManager interface {
// AppendEvent appends a single event to the stream
// Uses Redis RPush for O(1) append performance
// All event types (thinking, tool_call, references, complete) use this method
AppendEvent(ctx context.Context, sessionID, messageID string, event StreamEvent) error
// GetEvents gets events starting from offset
// Uses Redis LRange for incremental reads
// Returns: events slice, next offset for subsequent reads, error
GetEvents(ctx context.Context, sessionID, messageID string, fromOffset int) ([]StreamEvent, int, error)
// AppendSteerEvents appends control events (steer instructions) to a
// dedicated per-run sub-list that is never surfaced on the SSE stream.
// Producers are incoming "append a message to the running turn" HTTP
// requests; the only consumer is the running turn itself.
AppendSteerEvents(ctx context.Context, sessionID, messageID string, events []StreamEvent) error
// GetSteerEvents drains the steer sub-list starting fromOffset, mirroring
// GetEvents semantics. An absent list is an empty result, not an error.
GetSteerEvents(ctx context.Context, sessionID, messageID string, fromOffset int) ([]StreamEvent, int, error)
// UpdateSteerEventData merges keys into a queued steer event's Data map
// (promote to inject, mark consumed). Returns false when the event is
// missing. Implementations must apply the change atomically so a
// concurrent AppendSteerEvents cannot be lost.
UpdateSteerEventData(
ctx context.Context, sessionID, messageID, eventID string, data map[string]interface{},
) (bool, error)
// DeleteSteerEvent removes a queued steer event by ID. Returns false when
// the event is missing. Used when the user dismisses an overlay item.
DeleteSteerEvent(ctx context.Context, sessionID, messageID, eventID string) (bool, error)
// SetLiveRun records which assistant message is currently generating for
// a session. It is exclusive: if another assistant is already live, it
// returns an error so executeQA cannot start a second engine. Calling it
// again for the same assistant is a no-op. Follow-up handoff that must
// replace the previous run uses ClaimLiveRun.
SetLiveRun(ctx context.Context, sessionID, assistantMessageID, requestID string) error
// ClaimLiveRun overwrites the live-run marker. Used when a finishing
// turn hands the session to a follow-up before it CAS-clears itself.
ClaimLiveRun(ctx context.Context, sessionID, assistantMessageID, requestID string) error
// GetLiveRun returns the session's generating assistant message, or empty
// strings when no run is marked live.
GetLiveRun(ctx context.Context, sessionID string) (assistantMessageID, requestID string, err error)
// ClearLiveRun drops the marker, but only when it still points at
// assistantMessageID: a follow-up run may already have replaced it.
ClearLiveRun(ctx context.Context, sessionID, assistantMessageID string) error
}
StreamManager stream manager interface - minimal append-only design All stream state is managed through events: metadata, references, completion, etc.
type SyncLogRepository ¶
type SyncLogRepository interface {
// Create inserts a new sync log entry
Create(ctx context.Context, log *types.SyncLog) error
// FindByID retrieves a sync log by ID
FindByID(ctx context.Context, id string) (*types.SyncLog, error)
// FindByDataSource lists sync logs for a data source with pagination
FindByDataSource(ctx context.Context, dsID string, limit int, offset int) ([]*types.SyncLog, error)
// FindLatest retrieves the most recent sync log for a data source
FindLatest(ctx context.Context, dsID string) (*types.SyncLog, error)
// HasRunningSync checks if a data source has any sync currently in "running" status.
// Used to prevent overlapping sync executions.
HasRunningSync(ctx context.Context, dsID string) (bool, error)
// Update updates an existing sync log entry
Update(ctx context.Context, log *types.SyncLog) error
// UpdateResult updates only fields produced by a sync run.
UpdateResult(ctx context.Context, log *types.SyncLog) error
// CancelPendingByDataSource marks all non-terminal sync logs for a data source as canceled.
CancelPendingByDataSource(ctx context.Context, dsID string) error
// CleanupOldLogs deletes sync logs older than the retention period
CleanupOldLogs(ctx context.Context, retentionDays int) error
}
SyncLogRepository defines database access patterns for sync logs
type SystemSettingRepository ¶
type SystemSettingRepository interface {
// Get fetches a row by key. Returns (nil, nil) when the key is not
// present — callers fall back to ENV / default at the service layer.
Get(ctx context.Context, key string) (*types.SystemSetting, error)
// List returns every row. Used by the management UI to render the
// settings page; no pagination yet (the registry is small — single
// digits in P1, expected to stay double-digits long-term).
List(ctx context.Context) ([]*types.SystemSetting, error)
// Upsert writes a row keyed by Key. Insert if missing, update if
// present. Used by SystemSettingService.Update on every save.
Upsert(ctx context.Context, s *types.SystemSetting) error
// Delete removes the row by key. Returns (true, nil) if a row was
// deleted, (false, nil) if no row matched (idempotent — service
// layer treats this as a no-op for audit purposes). Real DB errors
// surface as (_, err).
Delete(ctx context.Context, key string) (bool, error)
}
SystemSettingRepository is the storage layer for the platform-wide system_settings table. All methods are system-scoped — there is no tenant_id; rows are global to the deployment.
type SystemSettingService ¶
type SystemSettingService interface {
// GetInt returns the resolved int64 value for `key`.
//
// envName is the legacy environment-variable name to consult when
// the DB row is absent ("" means the key has no ENV fallback).
// def is the built-in default used when both DB and ENV miss.
//
// Errors at the DB layer degrade gracefully: the function logs a
// warning and falls through to ENV / default rather than returning
// an error to upstream business code (which would have to bubble
// it through every caller — we'd rather mis-serve a request with
// the default than 500 a file upload).
GetInt(ctx context.Context, key string, envName string, def int64) int64
GetString(ctx context.Context, key string, envName string, def string) string
GetBool(ctx context.Context, key string, envName string, def bool) bool
// GetStringList resolves a comma-separated list of strings. envName
// is treated as a comma-separated string at the ENV level (mirrors
// the legacy SSRF_WHITELIST format). The slice returned is always
// non-nil so callers can iterate without a nil check.
GetStringList(ctx context.Context, key string, envName string, def []string) []string
// List, Get, Update are the management-CRUD surface called by the
// SystemAdmin handlers (gated to user.is_system_admin = true at the
// router layer). Update emits an audit log on success.
List(ctx context.Context) ([]*types.SystemSetting, error)
Get(ctx context.Context, key string) (*types.SystemSetting, error)
// Update writes a new value for `key`, validating that:
// 1. key is in the in-code registry (rejects 400 otherwise — UI
// cannot inject arbitrary keys),
// 2. rawValue's Go type matches the registry's expected type
// (e.g. int64 / float64 for "int", string for "string", bool
// for "bool"),
// 3. the actor is captured from ctx (UserIDFromContext) and
// written to last_modified_by + the audit log.
// Returns the persisted row on success.
Update(ctx context.Context, key string, rawValue any) (*types.SystemSetting, error)
// Reset removes the DB override for `key` so the resolver falls
// back to ENV / built-in default. Idempotent: deleting a key that
// was never persisted returns nil. Emits an audit row on actual
// deletions, invalidates the local cache, and publishes to peers.
// Returns an error only when the DB write itself fails; callers
// must NOT translate "no row" into a 404 — that's the success
// path for an idempotent reset.
Reset(ctx context.Context, key string) error
// SubscribeRedis is a P2 hook: when implemented, it will subscribe
// to a "weknora:system_settings:changed" channel and invalidate any
// in-memory cache on receipt. P1 implementations may return a no-op
// because there is no cache yet. Keeping the method in the interface
// now means P2 can drop in a real implementation without changing
// the container wiring.
SubscribeRedis(ctx context.Context) error
}
SystemSettingService exposes both the 3-tier resolver (used by production code paths that consume settings) and the management CRUD (used by the SystemAdmin UI).
3-tier resolver priority: DB > ENV > built-in default. The service owns the registry of legal keys; reading or writing an unknown key returns an error from Update / falls through to default for Get.
P1 ships with no in-memory cache: every GetXxx hits the DB. The callers (file-upload size check, etc.) are not on a hot path so the extra ~1ms is negligible. P2 may add Redis pubsub + a TTL cache; the SubscribeRedis hook below is a placeholder for that.
type TaskDeadLetterRepository ¶
type TaskDeadLetterRepository interface {
// Insert records one dead letter. Best-effort caller: the asynq
// middleware ignores the error so a failed insert never masks the
// underlying task error.
Insert(ctx context.Context, dl *types.TaskDeadLetter) error
// ListByScope returns dead letters for the given scope tuple,
// newest-first, paginated by failed-id cursor. `cursor` is the
// stringified id of the oldest entry from the previous page; "" =
// from the newest. Empty nextCursor = end of stream. `limit` is
// clamped to [1, 200].
ListByScope(ctx context.Context, scope, scopeID, cursor string, limit int) ([]*types.TaskDeadLetter, string, error)
// ListByTaskType returns dead letters for the given task_type,
// newest-first, with the same cursor semantics as ListByScope.
ListByTaskType(ctx context.Context, taskType, cursor string, limit int) ([]*types.TaskDeadLetter, string, error)
// DeleteByID drops a single dead letter (e.g. after operators have
// requeued the task manually).
DeleteByID(ctx context.Context, id int64) error
}
TaskDeadLetterRepository persists rows for the generic task dead-letter archive (`task_dead_letters`). Two writers exist: the asynq dead-letter middleware (one row per archived asynq task), and the service-level retry handlers (one row per in-batch op that exhausted its consumer-defined retry budget — wiki ingest is the current case).
Reads are operator-driven: list by scope to triage a single KB, list by task_type for cross-KB symptom hunting. No TTL.
type TaskEnqueuer ¶
type TaskEnqueuer interface {
Enqueue(task *asynq.Task, opts ...asynq.Option) (*asynq.TaskInfo, error)
}
TaskEnqueuer abstracts task enqueueing. *asynq.Client satisfies this interface. For Lite mode (no Redis), a synchronous implementation dispatches tasks inline.
type TaskHandler ¶
type TaskHandler interface {
// Handle handles the task
Handle(ctx context.Context, t *asynq.Task) error
}
TaskHandler is a interface for handling asynchronous tasks
type TaskInspector ¶
type TaskInspector interface {
// CancelTasksForKnowledge removes pending/scheduled/retry tasks
// whose payload references the given knowledge ID, and signals
// active workers running such tasks to stop. Returns rough
// counts of (deletedFromQueue, activeCancelled) for observability.
// Errors are returned but callers should treat the operation as
// best-effort: the row-level abort flag remains the source of
// truth, this just prevents wasted work.
CancelTasksForKnowledge(ctx context.Context, knowledgeID string) (deleted int, cancelled int, err error)
// HasQueuedTasksForKnowledge reports whether any pending / scheduled
// / retry / active task referencing the given knowledge ID still
// lives in the queue backend. It is the read-only counterpart of
// CancelTasksForKnowledge: the housekeeping sweep calls it before
// flipping a long-idle "processing"/"finalizing" row to "failed" so
// it can tell a genuinely orphaned row (no task anywhere) from one
// whose enrichment subtasks are merely backlogged behind a busy
// queue (no span heartbeat yet because no worker has picked them up).
//
// Best-effort and short-circuiting: it returns true as soon as the
// first match is seen. On backend error it returns (false, err);
// callers decide the fail-safe direction. Lite mode (no Redis)
// always returns false — inline executors never queue, so the
// span/updated_at checks remain authoritative there.
HasQueuedTasksForKnowledge(ctx context.Context, knowledgeID string) (bool, error)
// QueuedKnowledgeIDs returns every knowledge ID that a pending /
// scheduled / retry / active task of those same types references, in
// one pass over the queues — for answering many documents at once
// where HasQueuedTasksForKnowledge would rescan per document. Unlike
// that probe it returns list errors instead of reading them as "no
// match". Lite mode returns an empty set.
QueuedKnowledgeIDs(ctx context.Context) (map[string]struct{}, error)
// HasQueuedDeleteTasksForKnowledge reports whether any pending /
// scheduled / retry / active knowledge:list_delete task still covers
// the given knowledge ID. The delete sweep calls it before recovering
// a stranded "deleting" row: the delete task is enqueued with a batch
// payload (knowledge_ids list) that the per-parse matcher above cannot
// see, so without this probe the sweep could not tell a stranded delete
// from one that is merely backlogged behind a busy maintenance queue.
//
// Same fail-safe contract: (false, err) on backend error, callers defer;
// Lite mode always returns false — inline deletes never queue.
HasQueuedDeleteTasksForKnowledge(ctx context.Context, knowledgeID string) (bool, error)
// QueueStats returns a read-only depth snapshot for every queue this
// application enqueues into, for the System Admin runtime dashboard.
//
// The `supported` bool reports whether queue inspection is available
// on the current backend: true in asynq/Redis mode, false in Lite
// mode (no Redis) where tasks run inline and no queue exists. Callers
// use it to render an "unavailable in this deployment" state rather
// than an empty table.
//
// Best-effort: a per-queue backend error is logged and that queue is
// surfaced as a zeroed row (so the full lane set stays visible) rather
// than failing the whole call.
QueueStats(ctx context.Context) (stats []types.QueueStat, supported bool, err error)
// WorkerServerStats returns live asynq server heartbeats across all
// replicas. The runtime dashboard uses them to aggregate actual cluster
// capacity and busy workers for each configured pool.
WorkerServerStats(ctx context.Context) (stats []types.WorkerServerStat, supported bool, err error)
}
TaskInspector abstracts queue inspection / cancellation against the task backend. It is best-effort: implementations may scan a finite number of tasks per call and return whatever count they could affect. Lite mode (no Redis) ships a no-op implementation because SyncTaskExecutor dispatches inline goroutines that cannot be dequeued before they start.
Use cases today: user-initiated cancel of an in-progress knowledge parse, which must remove downstream multimodal / post-process / question / summary tasks already enqueued against the same knowledge_id, plus signal active workers to stop at their next checkpoint.
type TaskPendingOpsClaimableCounter ¶
type TaskPendingOpsClaimableCounter interface {
ClaimableCount(ctx context.Context, taskType, scope, scopeID string, staleBefore time.Time) (int64, error)
}
TaskPendingOpsClaimableCounter counts the distinct keys ClaimBatch can claim, excluding every key with a fresh claim (including late siblings). PendingCount remains the count of all rows for crash-recovery consumers.
type TaskPendingOpsDrainer ¶
type TaskPendingOpsDrainer interface {
DrainUnclaimedAndRelease(
ctx context.Context, taskType, scope, scopeID, op string, staleBefore time.Time,
) ([]string, error)
}
TaskPendingOpsDrainer is an optional extension for consumers that must give up on a queue lane. DrainUnclaimedAndRelease deletes the lane's op rows for documents no live batch holds (no row claimed at or after staleBefore) and releases one finalizing slot per such document, atomically; it returns the released dedup keys.
type TaskPendingOpsFinalizingSeeder ¶
type TaskPendingOpsFinalizingSeeder interface {
SeedKnowledgeFinalizingWithPendingOp(
ctx context.Context,
knowledgeID string,
expectedSubtasks int,
op *types.TaskPendingOp,
) (promoted bool, err error)
}
TaskPendingOpsFinalizingSeeder atomically hands a processing knowledge row to the asynchronous finalizing pipeline while persisting the durable pending operation that owns one of its subtask slots.
type TaskPendingOpsKnowledgeBaseGuard ¶
type TaskPendingOpsKnowledgeBaseGuard interface {
EnqueueIfKnowledgeBaseActive(ctx context.Context, op *types.TaskPendingOp) (accepted bool, err error)
}
TaskPendingOpsKnowledgeBaseGuard atomically persists a KB-scoped operation only while its knowledge base is still active. Implementations must serialize the active-KB check with soft deletion so a detached worker cannot recreate durable work after the deletion scrub has finished.
type TaskPendingOpsRepository ¶
type TaskPendingOpsRepository interface {
// Enqueue inserts a single op. The caller fills in TenantID, TaskType,
// Scope, ScopeID, Op, DedupKey, Payload; ID, FailCount, EnqueuedAt
// are server-side defaults.
Enqueue(ctx context.Context, op *types.TaskPendingOp) error
// PeekBatch returns up to `limit` rows for the given queue tuple,
// ordered by fail_count ASC, id ASC (least-failed first; FIFO among
// rows with the same fail_count). Rows are NOT removed — callers must
// DeleteByIDs once the ops have been processed (or IncrFailCount and
// leave them for the next pass).
PeekBatch(ctx context.Context, taskType, scope, scopeID string, limit int) ([]*types.TaskPendingOp, error)
// ClaimBatch atomically claims eligible rows for the tuple, grouped by
// dedup_key, and returns them with claimed_at set to now. `limit`
// counts DISTINCT dedup_keys (documents), NOT rows: ALL eligible rows
// sharing a chosen dedup_key are claimed together so a document with
// several queued ops is never split across two concurrent batches.
// A row is eligible when it is unclaimed (claimed_at IS NULL) or its
// claim is stale (claimed_at < staleBefore) — the latter recovers rows
// abandoned by a crashed worker. Keys are selected least-failed first
// (then oldest id) so a retried row cannot starve never-attempted
// work. On Postgres the per-key anchor row is locked with FOR UPDATE
// SKIP LOCKED so concurrent claimers take disjoint key sets without
// blocking or double-claiming.
//
// Claimed rows are NOT removed: the consumer must DeleteByIDs on
// success, or ReleaseByIDs to hand a still-retryable row back to the
// pool (otherwise it stays claimed until staleBefore elapses).
ClaimBatch(ctx context.Context, taskType, scope, scopeID string, limit int, staleBefore time.Time) ([]*types.TaskPendingOp, error)
// ReleaseByIDs clears claimed_at (back to NULL) for the given rows so
// a claimed-but-not-consumed row becomes immediately eligible for the
// next ClaimBatch. No-op for empty input. Used by the wiki retry path
// to re-queue a transiently-failed op without waiting for its claim to
// go stale. Harmless on rows that were never claimed (Lite mode).
ReleaseByIDs(ctx context.Context, ids []int64) error
// DeleteByIDs removes the given rows. No-op for empty input. Used
// to consume a successfully-processed batch, and to drop ops that
// have been moved to task_dead_letters.
DeleteByIDs(ctx context.Context, ids []int64) error
// IncrFailCount increments fail_count for one row and returns the
// new value. Returns (0, nil) if the row does not exist (race with
// DeleteByIDs is benign).
IncrFailCount(ctx context.Context, id int64) (int, error)
// PendingCount returns the number of rows currently queued for the
// given tuple. Cheap (covered by idx_task_pending_ops_scope) and
// used by the wiki ingest follow-up scheduler.
PendingCount(ctx context.Context, taskType, scope, scopeID string) (int64, error)
// DeleteByDedupKey removes rows for the tuple whose DedupKey
// matches. If `op` is non-empty, only rows with that exact op are
// removed (this lets wiki ingest scrub queued "ingest" ops while
// preserving "retract" ops for the same knowledge — retract is
// still needed to clean up wiki pages after the source doc is
// deleted). If `op` is empty, every matching row is removed
// regardless of op.
DeleteByDedupKey(ctx context.Context, taskType, scope, scopeID, dedupKey, op string) error
}
TaskPendingOpsRepository persists rows for the generic task pending queue (`task_pending_ops`). The queue is the durable replacement for the Redis-list-backed wiki:pending:<kbID> queue. It is intentionally stateless about consumer semantics: the (TaskType, Scope, ScopeID) tuple is the only routing primitive the repository understands; deduplication, batching, and retry policy live in the consumer.
Concurrency model: two consumption primitives coexist.
- PeekBatch does NOT take row locks; consumers enforce per-(scope_id) serialization out-of-band (e.g. an external Redis lock). This is the original primitive and is still used by single-process (Lite) mode.
- ClaimBatch DOES take row locks (SELECT ... FOR UPDATE SKIP LOCKED on Postgres) and marks rows with claimed_at, so multiple concurrent consumers of the same tuple pull DISJOINT rows without an external lock. This is what lets the wiki pipeline drop its exclusive per-KB batch lock and spread one KB's backlog across the whole worker pool.
type TaskPendingOpsScopeCleaner ¶
type TaskPendingOpsScopeCleaner interface {
DeleteByScope(ctx context.Context, scope, scopeID string) error
}
TaskPendingOpsScopeCleaner is an optional extension for callers that need to discard every durable pending operation owned by a deleted scope. It is intentionally separate from TaskPendingOpsRepository so alternate queue implementations and existing test doubles are not forced to implement a lifecycle-only operation.
type TaskPendingOpsTenantLiveness ¶
type TaskPendingOpsTenantLiveness interface {
HasActiveTenant(ctx context.Context, tenantID uint64) (bool, error)
}
TaskPendingOpsTenantLiveness reports whether a tenant is still alive (not soft-deleted). Wiki task consumers use it to guarantee a deleted tenant never triggers new model requests: the check runs at task entry (ingest and finalize) and inside the guarded enqueue, alongside the KB-active check — tenant soft-deletion removes the workspace without touching its knowledge bases or durable pending ops.
type TemporaryDocumentRepository ¶
type TemporaryDocumentRepository interface {
Create(ctx context.Context, document *types.TemporaryDocument) error
GetByID(ctx context.Context, tenantID uint64, documentID string) (*types.TemporaryDocument, error)
GetScoped(ctx context.Context, tenantID uint64, sessionID, documentID string) (*types.TemporaryDocument, error)
ListScoped(ctx context.Context, tenantID uint64, sessionID string) ([]*types.TemporaryDocument, error)
MarkProcessing(ctx context.Context, tenantID uint64, documentID string, startedAt time.Time) error
MarkReady(ctx context.Context, tenantID uint64, documentID, content string, chunks, imageRefs, metadata types.JSON, tokenCount, chunkCount int, readyAt time.Time) error
MarkFailed(ctx context.Context, tenantID uint64, documentID, message string) error
DeleteScoped(ctx context.Context, tenantID uint64, sessionID, documentID string) error
ListExpired(ctx context.Context, before time.Time, limit int) ([]*types.TemporaryDocument, error)
}
type TemporaryDocumentService ¶
type TemporaryDocumentService interface {
Create(ctx context.Context, tenantID uint64, sessionID, fileName, mimeType string, fileSize int64, reader io.Reader, options types.TemporaryDocumentCreateOptions) (*types.TemporaryDocument, error)
Get(ctx context.Context, tenantID uint64, sessionID, documentID string) (*types.TemporaryDocument, error)
OpenFile(ctx context.Context, tenantID uint64, sessionID, documentID string) (io.ReadCloser, string, error)
List(ctx context.Context, tenantID uint64, sessionID string) ([]*types.TemporaryDocument, error)
Delete(ctx context.Context, tenantID uint64, sessionID, documentID string) error
ResolveForPrompt(ctx context.Context, tenantID uint64, sessionID string, documentIDs []string, query string) (*types.TemporaryDocumentPromptResult, error)
DescribeForAgent(ctx context.Context, tenantID uint64, sessionID string,
documentIDs []string) (*types.TemporaryDocumentPromptResult, error)
Process(ctx context.Context, task *asynq.Task) error
CleanupExpired(ctx context.Context) error
}
type TenantAPIKeyCreateResult ¶
type TenantAPIKeyCreateResult struct {
APIKey *types.TenantAPIKey
Token string
}
type TenantAPIKeyRepository ¶
type TenantAPIKeyRepository interface {
CreateAPIKey(ctx context.Context, key *types.TenantAPIKey) error
GetAPIKeyByHash(ctx context.Context, hash string) (*types.TenantAPIKey, error)
ListAPIKeys(ctx context.Context, tenantID uint64) ([]*types.TenantAPIKey, error)
ListPlatformAPIKeys(ctx context.Context) ([]*types.TenantAPIKey, error)
UpdateAPIKey(ctx context.Context, tenantID uint64, id uint64, key *types.TenantAPIKey) (*types.TenantAPIKey, error)
RevokeAPIKey(ctx context.Context, tenantID uint64, id uint64) error
RevokePlatformAPIKey(ctx context.Context, id uint64) error
UpdateAPIKeyHash(ctx context.Context, id uint64, hash string) error
UpdateAPIKeyLastUsed(ctx context.Context, id uint64, at time.Time) error
// ListKeysWithPlaceholderHash returns keys whose key_hash is still the
// migration placeholder (never authenticated since the 000065 upgrade),
// so the real SHA-256 hash can be computed and backfilled.
ListKeysWithPlaceholderHash(ctx context.Context) ([]*types.TenantAPIKey, error)
// HasKeysWithPlaceholderHash is a cheap existence probe used at startup
// to skip loading/decrypting api_key rows once backfill is complete.
HasKeysWithPlaceholderHash(ctx context.Context) (bool, error)
}
type TenantAPIKeyService ¶
type TenantAPIKeyService interface {
CreateAPIKey(ctx context.Context, req TenantAPIKeyCreateRequest) (*TenantAPIKeyCreateResult, error)
AuthenticateAPIKey(ctx context.Context, token string) (*types.TenantAPIKey, error)
ListAPIKeys(ctx context.Context, tenantID uint64) ([]*types.TenantAPIKey, error)
ListPlatformAPIKeys(ctx context.Context) ([]*types.TenantAPIKey, error)
UpdateAPIKey(ctx context.Context, req TenantAPIKeyUpdateRequest) (*types.TenantAPIKey, error)
RevokeAPIKey(ctx context.Context, tenantID uint64, id uint64) error
RevokePlatformAPIKey(ctx context.Context, id uint64) error
// BackfillMissingKeyHashes computes and persists the SHA-256 key_hash
// for legacy keys still carrying the migration placeholder.
// Returns the number of keys backfilled.
BackfillMissingKeyHashes(ctx context.Context) (int, error)
}
type TenantAPIKeyUpdateRequest ¶
type TenantAPIKeyUpdateRequest struct {
TenantID uint64
APIKeyID uint64
Name string
FullAccess bool
KnowledgeBaseIDs []string
Capabilities []string
ExpiresAt *time.Time
}
TenantAPIKeyUpdateRequest 修改已创建租户 API Key 的可配置属性。 配置语义与创建接口一致:FullAccess 为 true 时忽略细粒度能力和知识库范围。
type TenantDisabledSharedAgentRepository ¶
type TenantDisabledSharedAgentRepository interface {
// ListDisabledOwnAgentIDs returns agent IDs that this tenant has disabled for their own agents (source_tenant_id = tenant_id)
}
TenantDisabledSharedAgentRepository stores per-tenant "disabled" agents (hidden from conversation dropdown; own and shared)
type TenantInvitationRepository ¶
type TenantInvitationRepository interface {
// Create inserts a new pending invitation. Returns the conflict
// error sentinel (ErrPendingInvitationExists) if the (tenant_id,
// invitee) partial unique index rejects the insert.
Create(ctx context.Context, inv *types.TenantInvitation) error
// GetByID fetches an invitation by surrogate id, or (nil, nil) if
// missing. Used by Accept/Decline/Revoke handlers.
GetByID(ctx context.Context, id uint64) (*types.TenantInvitation, error)
// GetPendingByPair returns the in-flight pending invitation for
// (tenant, invitee) or (nil, nil) when none exists. Used by the
// service layer to short-circuit duplicate Create calls before
// they hit the unique index.
GetPendingByPair(ctx context.Context, tenantID uint64, inviteeUserID string) (*types.TenantInvitation, error)
// GetActiveByToken looks up the share-link row matching the
// supplied plaintext token. Returns (nil, nil) if no row matches
// or the row is no longer pending. "Active" rather than "pending
// for a single user" because share-link rows are multi-use:
// AcceptByToken does NOT consume them, only Revoke or expiry
// removes them from circulation.
GetActiveByToken(ctx context.Context, token string) (*types.TenantInvitation, error)
// ListByTenant returns invitations for the tenant ordered by
// id DESC (newest first). includeTerminal controls whether
// non-pending rows (accepted/declined/revoked/expired) are
// returned — the management UI shows pending by default, with
// a "history" toggle for the rest.
ListByTenant(ctx context.Context, tenantID uint64, includeTerminal bool) ([]*types.TenantInvitation, error)
// CountByTenantList counts invitation rows matching the same filter as
// ListByTenant (tenant_id + optional pending-only vs include-terminal).
CountByTenantList(ctx context.Context, tenantID uint64, includeTerminal bool) (int64, error)
// ListByTenantPage returns invitations for the tenant with id DESC
// paging, same filtering semantics as ListByTenant.
ListByTenantPage(ctx context.Context, tenantID uint64, includeTerminal bool, offset, limit int) ([]*types.TenantInvitation, error)
// ListByInvitee returns invitations addressed to the user across
// all tenants. Ordered by id DESC. Used both by the inbox page
// (includeTerminal=false) and by /me/invitations/history if/when
// we expose history (includeTerminal=true).
ListByInvitee(ctx context.Context, inviteeUserID string, includeTerminal bool) ([]*types.TenantInvitation, error)
// CountPendingByInvitee returns the number of pending invitations
// for the user. Used by the avatar-row badge so the UI doesn't
// have to fetch the full list just to render a count.
CountPendingByInvitee(ctx context.Context, inviteeUserID string) (int64, error)
// MarkStatusIfPending atomically transitions a row from pending
// to the supplied status. respondedAt is recorded as the
// transition time. Returns gorm.ErrRecordNotFound when the row
// does not exist or is already in a non-pending state, which the
// service layer maps to ErrInvitationNotPending — that error is
// the "stale state machine" signal callers care about.
MarkStatusIfPending(
ctx context.Context,
id uint64,
status types.TenantInvitationStatus,
respondedAt time.Time,
) error
// SweepExpired transitions all pending rows whose expires_at is
// before `now` into the expired state. Returns the affected row
// count so callers can decide whether to emit per-row audit
// events (we don't — the volume could be high and the audit-log
// table already records the original invitation_sent rows).
SweepExpired(ctx context.Context, now time.Time) (int64, error)
// IncrementAcceptedCount atomically bumps accepted_count by 1.
// Used by AcceptByToken so the management UI can show "N 人已通过
// 此链接加入" for share-link rows. Per-user invitations also call
// this on accept; the count just caps at 1 there.
IncrementAcceptedCount(ctx context.Context, id uint64) error
}
TenantInvitationRepository persists tenant_invitations rows that capture the "pending invite" intent before it becomes a real tenant_members row. All read methods skip GORM soft-deleted rows.
type TenantInvitationService ¶
type TenantInvitationService interface {
// Create issues a new pending invitation for (tenantID, inviteeUserID)
// with the given role. Caller passes the inviter user id (may be
// nil for service-internal flows). Returns the freshly created row
// or a sentinel error (ErrPendingInvitationExists / ErrAlreadyMember /
// ErrInvalidTenantRole) for the common conflicts.
Create(
ctx context.Context,
tenantID uint64,
inviteeUserID string,
role types.TenantRole,
invitedBy *string,
message string,
) (*types.TenantInvitation, error)
// Accept transitions the pending row into accepted AND creates the
// active tenant_members row in the same transaction. The callerUserID
// MUST equal inv.InviteeUserID — the service rejects anyone else.
// Returns the freshly created membership for the response shape.
Accept(ctx context.Context, invID uint64, callerUserID string) (*types.TenantMember, error)
// Decline transitions the pending row into declined. Same caller
// rule as Accept: only the invitee themselves.
Decline(ctx context.Context, invID uint64, callerUserID string) error
// Revoke transitions the pending row into revoked. Callable by an
// Owner of the tenant the invitation belongs to (the handler
// applies the Owner gate via g.Owner() route middleware, so this
// method does not re-check role).
Revoke(ctx context.Context, invID uint64) error
// GetByID returns an invitation or (nil, nil). Used by handlers to
// authorise per-row operations (e.g. is the caller the invitee?).
GetByID(ctx context.Context, invID uint64) (*types.TenantInvitation, error)
// ListByTenant returns invitations for a tenant. Always runs the
// lazy expiry sweep before reading so stale-pending rows surface
// as expired in the UI.
ListByTenant(ctx context.Context, tenantID uint64, includeTerminal bool) ([]*types.TenantInvitation, error)
// ListTenantInvitationsPage paginates invitations for the tenant
// management UI after the lazy sweep (same filtering as ListByTenant).
ListTenantInvitationsPage(ctx context.Context, tenantID uint64, includeTerminal bool, page, pageSize int) ([]*types.TenantInvitation, int64, error)
// ListByInvitee returns invitations addressed to the user across
// all tenants. Always runs the lazy sweep first.
ListByInvitee(ctx context.Context, inviteeUserID string, includeTerminal bool) ([]*types.TenantInvitation, error)
// CountPendingByInvitee returns the user's pending invitation count
// after running the lazy sweep, so the inbox badge can poll a
// lightweight endpoint without paginating the full list.
CountPendingByInvitee(ctx context.Context, inviteeUserID string) (int64, error)
// the tenant. The plaintext token is persisted on the row and also
// returned for the handler to compose the registration URL — the
// token stays available via list/get for as long as the row is
// pending so Owners can re-share without "copy now or lose it"
// pressure. Multiple share-link rows can coexist on the same
// tenant (different roles, or just multiple campaigns).
CreateShareLink(
ctx context.Context,
tenantID uint64,
role types.TenantRole,
invitedBy *string,
message string,
) (inv *types.TenantInvitation, plainToken string, err error)
// LookupByToken resolves a plaintext token to its active share-link
// row. Returns ErrInvitationTokenInvalid for unknown / expired /
// revoked tokens to avoid leaking which tokens used to exist. The
// row is NOT consumed; the same token can be looked up arbitrarily
// many times until it expires or the Owner revokes it.
LookupByToken(ctx context.Context, plainToken string) (*types.TenantInvitation, error)
// AcceptByToken creates a tenant_members row binding newUserID to
// the share-link's tenant + role. Unlike Accept, this does NOT flip
// the invitation row into a terminal state — share-link rows stay
// pending so subsequent invitees can also register through the
// same link. Idempotent: if the user already has membership, the
// existing row is returned. If the same user also has a pending direct
// invitation for the tenant, it is reconciled to accepted so the inbox
// cannot offer a second acceptance after membership already exists.
AcceptByToken(ctx context.Context, plainToken string, newUserID string) (*types.TenantMember, error)
// MarkPendingAcceptedIfExists transitions a per-user pending invitation
// into accepted when auto-accept joins the invitee without inbox
// confirmation. No-op when no pending row exists.
MarkPendingAcceptedIfExists(ctx context.Context, tenantID uint64, inviteeUserID string) error
}
TenantInvitationService is the business-logic layer over TenantInvitationRepository. It enforces the lifecycle invariants described in TenantInvitationStatus, runs the lazy expiry sweep, and brokers the accept-time hand-off into TenantMemberService so that "accept" produces both the invitation transition AND the tenant_members row in a single audit story.
type TenantMemberRepository ¶
type TenantMemberRepository interface {
// Create inserts a new active membership row. Caller is responsible
// for ensuring no other active row exists for the same (user, tenant)
// pair; the partial unique index will return a conflict error otherwise.
Create(ctx context.Context, member *types.TenantMember) error
// Get returns the active membership for the given (user, tenant) pair,
// or (nil, nil) if no such row exists.
Get(ctx context.Context, userID string, tenantID uint64) (*types.TenantMember, error)
// ListByUser returns every active membership owned by the given user,
// ordered by joined_at ascending.
ListByUser(ctx context.Context, userID string) ([]*types.TenantMember, error)
// ListByTenant returns every active membership inside the given tenant,
// ordered by joined_at ascending.
ListByTenant(ctx context.Context, tenantID uint64) ([]*types.TenantMember, error)
// CountFilteredByTenant counts active memberships in tenant. Optional
// search matches user email/username (join users table); empty search
// counts all memberships.
CountFilteredByTenant(ctx context.Context, tenantID uint64, search string) (int64, error)
// ListPagedByTenant returns active memberships sorted joined_at ASC, id ASC.
// search filters by user email/username (join users table); empty
// search lists all memberships in tenant.
ListPagedByTenant(ctx context.Context, tenantID uint64, search string, offset, limit int) ([]*types.TenantMember, error)
// UpdateRole changes the role of an existing active membership. Returns
// gorm.ErrRecordNotFound if no active row matches.
UpdateRole(ctx context.Context, userID string, tenantID uint64, role types.TenantRole) error
// SoftDelete marks the active membership as deleted. The user record
// itself is untouched.
SoftDelete(ctx context.Context, userID string, tenantID uint64) error
// CountActiveOwners reports how many active rows in the tenant carry
// the owner role. Used by service-layer invariant checks ("cannot
// remove the last owner").
CountActiveOwners(ctx context.Context, tenantID uint64) (int64, error)
// HasAnyMembers reports whether the tenant has at least one active
// membership. Used by the auth middleware to decide whether to
// auto-promote the first authenticating human in an API-key-only tenant.
HasAnyMembers(ctx context.Context, tenantID uint64) (bool, error)
// DemoteOwnerAtomically demotes an Owner to a non-Owner role inside
// a transaction that holds an UPDATE lock on the tenant's other
// active Owners, fixing the TOCTOU race where two concurrent
// demotions could leave the tenant ownerless. Returns the
// repo-level ErrLastOwner sentinel when no other Owner exists.
DemoteOwnerAtomically(ctx context.Context, userID string, tenantID uint64, newRole types.TenantRole) error
// RemoveOwnerAtomically soft-deletes an Owner row under the same
// lock as DemoteOwnerAtomically.
RemoveOwnerAtomically(ctx context.Context, userID string, tenantID uint64) error
}
TenantMemberRepository persists (user, tenant) membership rows that carry the per-tenant TenantRole.
All methods operate on active rows only (deleted_at IS NULL) unless the docstring explicitly says otherwise. Soft deletion is handled by GORM via the DeletedAt field on TenantMember.
type TenantMemberService ¶
type TenantMemberService interface {
// AddMember inserts a new active membership row. Returns an error if
// (user, tenant) already has an active membership.
AddMember(ctx context.Context, userID string, tenantID uint64, role types.TenantRole, invitedBy *string) (*types.TenantMember, error)
// EnsureOwner is an idempotent helper used by the registration flow:
// if the user already has an active membership in the tenant, return
// it; otherwise create one with role=owner. This is the common path
// for self-service registration where the registrant becomes the
// Owner of the tenant their account just created.
EnsureOwner(ctx context.Context, userID string, tenantID uint64) (*types.TenantMember, error)
// GetMembership returns the active (user, tenant) membership, or
// (nil, nil) if no such row exists.
GetMembership(ctx context.Context, userID string, tenantID uint64) (*types.TenantMember, error)
// ListByUser returns every active membership owned by the user.
ListByUser(ctx context.Context, userID string) ([]*types.TenantMember, error)
// ListByTenant returns every active membership inside the tenant.
ListByTenant(ctx context.Context, tenantID uint64) ([]*types.TenantMember, error)
// ListMembersPage lists members with pagination. Query matches email or
// username case-insensitively; empty query returns full tenant list slice.
ListMembersPage(ctx context.Context, tenantID uint64, query string, page, pageSize int) ([]*types.TenantMember, int64, error)
// HasAnyMembers reports whether the tenant has at least one active
// member. The auth middleware uses this to recover orphan tenants
// (e.g. API-key-only tenants that never had a human member): the
// first human authenticating into such a tenant is auto-promoted
// to Owner.
HasAnyMembers(ctx context.Context, tenantID uint64) (bool, error)
// UpdateRole changes the role of an existing membership while
// enforcing the "cannot demote the last active Owner" invariant.
UpdateRole(ctx context.Context, userID string, tenantID uint64, newRole types.TenantRole) error
// RemoveMember soft-deletes the membership while enforcing the
// "cannot remove the last active Owner" invariant.
RemoveMember(ctx context.Context, userID string, tenantID uint64) error
}
TenantMemberService is the business-logic layer over TenantMemberRepository. It enforces tenant-RBAC invariants such as "every tenant with members must keep at least one active Owner". HTTP handlers and other services call this interface rather than the repository directly so the invariants cannot be silently bypassed.
type TenantRepository ¶
type TenantRepository interface {
// CreateTenant creates a tenant
CreateTenant(ctx context.Context, tenant *types.Tenant) error
// GetTenantByID gets a tenant by ID
GetTenantByID(ctx context.Context, id uint64) (*types.Tenant, error)
// GetTenantsByIDs batches GetTenantByID; see TenantService.GetTenantsByIDs.
GetTenantsByIDs(ctx context.Context, ids []uint64) (map[uint64]*types.Tenant, error)
// ListTenants lists all tenants
ListTenants(ctx context.Context) ([]*types.Tenant, error)
// SearchTenants searches tenants with pagination and filters
SearchTenants(ctx context.Context, keyword string, tenantID uint64, page, pageSize int) ([]*types.Tenant, int64, error)
// UpdateTenant updates a tenant
UpdateTenant(ctx context.Context, tenant *types.Tenant) error
// DeleteTenant deletes a tenant
DeleteTenant(ctx context.Context, id uint64) error
// AdjustStorageUsed adjusts the storage used for a tenant
AdjustStorageUsed(ctx context.Context, tenantID uint64, delta int64) error
// BulkSetStorageQuota — see TenantService.BulkSetStorageQuota.
BulkSetStorageQuota(ctx context.Context, quotaBytes int64) (int64, error)
}
TenantRepository defines the tenant repository interface
type TenantService ¶
type TenantService interface {
// CreateTenant creates a tenant
CreateTenant(ctx context.Context, tenant *types.Tenant) (*types.Tenant, error)
// GetTenantByID gets a tenant by ID
GetTenantByID(ctx context.Context, id uint64) (*types.Tenant, error)
// GetTenantsByIDs batches GetTenantByID for multiple IDs in a single
// query. Returns a map keyed by tenant ID for O(1) lookup at the
// call site; missing tenants are simply absent from the map.
GetTenantsByIDs(ctx context.Context, ids []uint64) (map[uint64]*types.Tenant, error)
// ListTenants lists all tenants
ListTenants(ctx context.Context) ([]*types.Tenant, error)
// UpdateTenant updates a tenant
UpdateTenant(ctx context.Context, tenant *types.Tenant) (*types.Tenant, error)
// DeleteTenant deletes a tenant
DeleteTenant(ctx context.Context, id uint64) error
// ListAllTenants lists all tenants (for users with cross-tenant access permission)
ListAllTenants(ctx context.Context) ([]*types.Tenant, error)
// BulkSetStorageQuota overwrites every tenant's storage_quota with
// quotaBytes. Returns how many rows were affected. Used by the
// SystemAdmin "apply default to all tenants" action; bypasses the
// per-tenant whitelist on PUT /tenants/:id (which intentionally
// forbids storage_quota edits for Owners). quotaBytes must be > 0;
// callers are responsible for resolving GB→bytes.
BulkSetStorageQuota(ctx context.Context, quotaBytes int64) (int64, error)
// SearchTenants searches tenants with pagination and filters
SearchTenants(ctx context.Context, keyword string, tenantID uint64, page, pageSize int) ([]*types.Tenant, int64, error)
// GetTenantByIDForUser gets a tenant by ID with permission check
GetTenantByIDForUser(ctx context.Context, tenantID uint64, userID string) (*types.Tenant, error)
// GetWeKnoraCloudCredentials returns the decrypted WeKnoraCloud credentials for the current tenant.
GetWeKnoraCloudCredentials(ctx context.Context) *types.WeKnoraCloudCredentials
}
TenantService defines the tenant service interface
type UserRepository ¶
type UserRepository interface {
// CreateUser creates a user
CreateUser(ctx context.Context, user *types.User) error
// GetUserByID gets a user by ID
GetUserByID(ctx context.Context, id string) (*types.User, error)
// GetUsersByIDs batch-fetches users by id, returning a map keyed by
// user id. Missing ids are simply absent from the result.
GetUsersByIDs(ctx context.Context, ids []string) (map[string]*types.User, error)
// GetUserByEmail gets a user by email
GetUserByEmail(ctx context.Context, email string) (*types.User, error)
// GetUserByUsername gets a user by username
GetUserByUsername(ctx context.Context, username string) (*types.User, error)
// GetUserByTenantID gets the first user (owner) of a tenant
GetUserByTenantID(ctx context.Context, tenantID uint64) (*types.User, error)
// UpdateUser updates user data without changing account status; use SetUserActive for that.
UpdateUser(ctx context.Context, user *types.User) error
// SetUserActive atomically changes status and revokes sessions on disable,
// preserving at least one active system administrator.
SetUserActive(ctx context.Context, userID, actorID string, active bool) (*types.User, error)
// DeleteUser deletes a user
DeleteUser(ctx context.Context, id string) error
// ListUsers lists users with pagination
ListUsers(ctx context.Context, offset, limit int) ([]*types.User, error)
// ListSystemAdmins lists users where is_system_admin = true.
// Walks the partial-friendly idx_users_is_system_admin index. Returns
// the slice plus the total count for pagination metadata. Used by
// the system-admin management endpoint.
ListSystemAdmins(ctx context.Context, offset, limit int) ([]*types.User, int64, error)
// RevokeSystemAdmin removes system-admin privileges with the
// last-admin/self-revoke checks performed atomically.
RevokeSystemAdmin(ctx context.Context, userID, actorID string) (*types.User, error)
// SearchUsers searches users by username or email
SearchUsers(ctx context.Context, query string, limit int) ([]*types.User, error)
}
UserRepository defines the user repository interface
type UserResourceFavoriteRepository ¶
type UserResourceFavoriteRepository interface {
// List returns favorites for (userID, tenantID, resourceType), newest first.
List(ctx context.Context, userID string, tenantID uint64, resourceType string) ([]*types.UserResourceFavorite, error)
// Add upserts a favorite (no-op if already present). Returns whether
// a new row was actually inserted so the service layer can decide
// whether to emit an audit event.
Add(ctx context.Context, userID string, tenantID uint64, resourceType, resourceID string) (created bool, err error)
// Remove deletes a favorite; returns whether anything was deleted.
Remove(ctx context.Context, userID string, tenantID uint64, resourceType, resourceID string) (removed bool, err error)
// IsFavorite exists for the rare RPC where the caller wants a single
// boolean rather than the full list (e.g. a future GET-by-id endpoint).
IsFavorite(ctx context.Context, userID string, tenantID uint64, resourceType, resourceID string) (bool, error)
}
UserResourceFavoriteRepository is the storage contract for per-user favorited resources. Implementations are expected to be tenant-aware (queries always include tenant_id) so a user switching tenants sees only that tenant's favorites — matches the PK shape in migration 000047.
type UserResourceFavoriteService ¶
type UserResourceFavoriteService interface {
List(ctx context.Context, userID string, tenantID uint64, resourceType string) ([]*types.UserResourceFavorite, error)
Add(ctx context.Context, userID string, tenantID uint64, resourceType, resourceID string) error
Remove(ctx context.Context, userID string, tenantID uint64, resourceType, resourceID string) error
}
UserResourceFavoriteService wraps the repository with input validation (resource type allowlist, non-empty id) so the handler stays thin.
type UserService ¶
type UserService interface {
// Register creates a new user account
Register(ctx context.Context, req *types.RegisterRequest) (*types.User, error)
// Login authenticates a user and returns tokens
Login(ctx context.Context, req *types.LoginRequest) (*types.LoginResponse, error)
// GetOIDCAuthorizationURL builds the third-party OIDC authorization URL
GetOIDCAuthorizationURL(ctx context.Context, redirectURI string) (*types.OIDCAuthURLResponse, error)
// LoginWithOIDC exchanges the callback code, auto-provisions users if needed, and completes login.
// provisioning is the default tenant mode for a newly auto-created user
// (resolved by the caller from auth.default_tenant_mode).
LoginWithOIDC(ctx context.Context, code, redirectURI string, provisioning types.TenantProvisioningMode) (*types.OIDCCallbackResponse, error)
// GetUserByID gets a user by ID
GetUserByID(ctx context.Context, id string) (*types.User, error)
// GetUsersByIDs batch-fetches users by id, returning a map keyed by
// user id. Missing ids are simply absent from the result; the call
// is not an error when some ids resolve to no row. Used on hot list
// endpoints (tenant members, audit logs) to avoid N+1 queries.
GetUsersByIDs(ctx context.Context, ids []string) (map[string]*types.User, error)
// GetUserByEmail gets a user by email
GetUserByEmail(ctx context.Context, email string) (*types.User, error)
// GetUserByUsername gets a user by username
GetUserByUsername(ctx context.Context, username string) (*types.User, error)
// GetUserByTenantID gets the first user (owner) of a tenant
GetUserByTenantID(ctx context.Context, tenantID uint64) (*types.User, error)
// UpdateUser updates user data without changing account status; use SetUserActive for that.
UpdateUser(ctx context.Context, user *types.User) error
// DeleteUser deletes a user
DeleteUser(ctx context.Context, id string) error
// ChangePassword changes user password
ChangePassword(ctx context.Context, userID string, oldPassword, newPassword string) error
// AdminResetPassword replaces a user's password without requiring the old
// password and revokes all of that user's existing sessions. Callers must
// enforce the system-admin and cannot-reset-self guards before invoking it.
AdminResetPassword(ctx context.Context, userID string, newPassword string) error
// ValidatePassword validates user password
ValidatePassword(ctx context.Context, userID string, password string) error
// GenerateTokens generates access and refresh tokens for user
GenerateTokens(ctx context.Context, user *types.User) (accessToken, refreshToken string, err error)
// BuildLoginMemberships projects the user's tenant memberships into
// the login-response shape. activeTenant is reused (without an extra
// lookup) for the matching row's TenantName. The slice is guaranteed
// non-nil so callers can serialise it as an empty JSON array when the
// membership table is unavailable.
BuildLoginMemberships(ctx context.Context, user *types.User, activeTenant *types.Tenant) []types.Membership
// SwitchTenant issues a new token pair scoped to targetTenantID and
// returns the corresponding LoginResponse. The caller's previous
// refresh token (passed in for revocation) is invalidated. Membership
// is verified via the TenantMember service before tokens are issued.
// On success the target is recorded as the user's last-active-tenant
// preference (next login and refresh land there). A preference-write
// failure fails the switch so no token pair is issued.
SwitchTenant(ctx context.Context, user *types.User, targetTenantID uint64, currentRefreshToken string) (*types.LoginResponse, error)
// ValidateToken validates an access token. It returns the user
// referenced by the token plus the active tenant ID encoded in the
// JWT's `tenant_id` claim — the latter lets the auth middleware
// honour /auth/switch-tenant sessions that were minted with a
// non-home tenant. Falls back to user.TenantID when the claim is
// missing (old tokens issued before tenant-level RBAC).
ValidateToken(ctx context.Context, token string) (*types.User, uint64, error)
// GetAccessTokenByValue looks up the stored access-token row for a JWT
// string. The Token field is redacted. Used to bind a sandbox-terminal
// ticket to the minting session without putting the JWT in the ticket.
GetAccessTokenByValue(ctx context.Context, token string) (*types.AuthToken, error)
// GetAccessTokenByID looks up a stored access-token row by primary key.
// The Token field is redacted. Used by the sandbox-terminal recheck.
GetAccessTokenByID(ctx context.Context, id string) (*types.AuthToken, error)
// RefreshToken refreshes access token using refresh token
RefreshToken(ctx context.Context, refreshToken string) (accessToken, newRefreshToken string, err error)
// RevokeToken revokes a token
RevokeToken(ctx context.Context, token string) error
// Logout revokes every outstanding access/refresh token for the user
// identified by the presented JWT.
Logout(ctx context.Context, token string) error
// GetCurrentUser gets current user from context
GetCurrentUser(ctx context.Context) (*types.User, error)
// SearchUsers searches users by username or email
SearchUsers(ctx context.Context, query string, limit int) ([]*types.User, error)
// SetUserActive enables or disables a user account (SystemAdmin action).
// Disabling also revokes all of that user's outstanding tokens. The
// repository enforces self-disable and last-system-admin guards.
SetUserActive(ctx context.Context, userID, actorID string, active bool) (*types.User, error)
// ListAllUsers lists every platform user (across tenants) newest-first.
// Returns the page plus a has_more flag so the system-admin user-list UI
// can paginate without a second count query. The handler enforces the
// SystemAdmin gate, so the service does not duplicate it here.
ListAllUsers(ctx context.Context, offset, limit int) ([]*types.User, bool, error)
// ListSystemAdmins lists users with IsSystemAdmin=true.
// Returns the page of admins plus the total count (for pagination UI);
// callers pass offset/limit to page through results. Used by the
// /api/v1/system/admin/list endpoint, gated to SystemAdmin callers.
ListSystemAdmins(ctx context.Context, offset, limit int) ([]*types.User, int64, error)
// AdminCreateUser provisions a new local user on behalf of a
// SystemAdmin. When req.Password is nil, a random password is generated
// and returned exactly once as the second result. provisioning is
// resolved by the caller from the shared auth.default_tenant_mode policy.
AdminCreateUser(
ctx context.Context, req *types.AdminCreateUserRequest, provisioning types.TenantProvisioningMode,
) (*types.User, string, error)
// RevokeSystemAdmin removes system-admin privileges with the
// last-admin/self-revoke checks performed atomically.
RevokeSystemAdmin(ctx context.Context, userID, actorID string) (*types.User, error)
// UpdateUserPreferences partially updates the calling user's
// preferences blob (PATCH semantics: only keys present in `patch`
// overwrite existing values). Returns the updated, persisted prefs.
UpdateUserPreferences(ctx context.Context, userID string, patch types.UserPreferences) (types.UserPreferences, error)
}
UserService defines the user service interface
type VectorStoreRepository ¶
type VectorStoreRepository interface {
// Create creates a new vector store
Create(ctx context.Context, store *types.VectorStore) error
// GetByID retrieves a vector store by ID within a tenant scope
GetByID(ctx context.Context, tenantID uint64, id string) (*types.VectorStore, error)
// List lists all vector stores for a tenant
List(ctx context.Context, tenantID uint64) ([]*types.VectorStore, error)
// Update updates a vector store (only mutable fields: name)
Update(ctx context.Context, store *types.VectorStore) error
// UpdateConnectionConfig updates only the connection_config column
UpdateConnectionConfig(ctx context.Context, store *types.VectorStore) error
// Delete soft-deletes a vector store
Delete(ctx context.Context, tenantID uint64, id string) error
// ExistsByEndpointAndIndex checks if a store with the same endpoint and index already exists
ExistsByEndpointAndIndex(ctx context.Context, tenantID uint64, engineType types.RetrieverEngineType, endpoint string, indexName string) (bool, error)
}
VectorStoreRepository defines the repository interface for VectorStore CRUD.
type VectorStoreService ¶
type VectorStoreService interface {
// CreateStore validates and creates a new vector store.
CreateStore(ctx context.Context, store *types.VectorStore) error
// UpdateStore updates an existing vector store (name only).
UpdateStore(ctx context.Context, store *types.VectorStore) error
// DeleteStore deletes a vector store by tenant + id.
// Rejects deletion when any active knowledge base is bound to the store
// (binding guard); the caller must unbind or delete those KBs first.
DeleteStore(ctx context.Context, tenantID uint64, id string) error
// TestConnection tests connectivity to a vector database.
// Returns the detected server version on success (e.g., "7.10.1"), empty string if unknown.
//
// Validation-free: intended for trusted configs (env stores, stored
// configs already validated at create time). Handlers receiving raw
// user input MUST use TestRawConnection instead.
TestConnection(ctx context.Context, engineType types.RetrieverEngineType, config types.ConnectionConfig) (string, error)
// TestRawConnection validates raw user-supplied connection config
// (engine-type allowlist, required fields, SSRF policy) and then delegates
// to TestConnection. This is the entry point for unpersisted user input.
TestRawConnection(ctx context.Context, engineType types.RetrieverEngineType, config types.ConnectionConfig) (string, error)
// SaveDetectedVersion updates the connection_config.version for a stored vector store.
SaveDetectedVersion(ctx context.Context, store *types.VectorStore, version string) error
// ResolveStoreView returns the API-safe display projection of a single
// store ID, scoped to the given tenant. Tries DB stores first, then the
// cached env-store set. Returns types.UnavailableStoreDisplay() when the
// store cannot be resolved — callers should still succeed in such cases
// (the response carries a "unavailable" status signal for the UI).
//
// Never returns connection credentials in any form: the StoreDisplay
// payload carries only Name / Source / EngineType / Status.
ResolveStoreView(ctx context.Context, tenantID uint64, storeID string) (types.StoreDisplay, error)
// BatchResolveStoreView resolves multiple store IDs in a single DB read
// plus the cached env-store match. Returned map keys are the storeIDs
// originally requested; missing IDs map to types.UnavailableStoreDisplay().
//
// Intended for list endpoints that need store metadata for many KBs at
// once without incurring N+1 ResolveStoreView calls.
BatchResolveStoreView(ctx context.Context, tenantID uint64, storeIDs []string) (map[string]types.StoreDisplay, error)
// EnvDefaultStoreView returns the display payload for KBs that fall
// back to the env-configured store. Unlike DefaultStoreDisplay() in
// the types package, this method also fills the engine type
// (e.g. "postgres") so UIs can render the same badge shape for
// env-bound and user-bound KBs. Independent of ResolveStoreView so
// list paths can use it without violating the "no per-KB
// ResolveStoreView" invariant.
EnvDefaultStoreView(ctx context.Context) types.StoreDisplay
}
VectorStoreService defines the service interface for vector store management. Tenant isolation is enforced by the handler layer (getOwnedStore pattern).
type WeKnoraCloudService ¶
type WeKnoraCloudService interface {
// SaveCredentials 仅保存 APPID/APPSECRET 凭证到空间配置,不自动创建模型
SaveCredentials(ctx context.Context, appID, appSecret string) error
// CheckStatus 检查当前空间的 WeKnoraCloud 凭证是否可正常解密
// needsReinit=true 表示加密状态已损坏(salt 变更等),需要用户重新填写凭证
CheckStatus(ctx context.Context) (*types.WeKnoraCloudStatusResult, error)
}
WeKnoraCloudService 处理 WeKnoraCloud 凭证管理
type WebSearchProvider ¶
type WebSearchProvider interface {
// Name returns the name of the provider
Name() string
// Search performs a web search
Search(ctx context.Context, query string, maxResults int, includeDate bool) ([]*types.WebSearchResult, error)
}
WebSearchProvider defines the interface for web search providers
type WebSearchProviderRepository ¶
type WebSearchProviderRepository interface {
// Create creates a new web search provider
Create(ctx context.Context, provider *types.WebSearchProviderEntity) error
// GetByID retrieves a web search provider by ID within a tenant scope
GetByID(ctx context.Context, tenantID uint64, id string) (*types.WebSearchProviderEntity, error)
// GetDefault retrieves the default provider (is_default=true) for a tenant, or nil if none.
GetDefault(ctx context.Context, tenantID uint64) (*types.WebSearchProviderEntity, error)
// List lists all web search providers for a tenant
List(ctx context.Context, tenantID uint64) ([]*types.WebSearchProviderEntity, error)
// Update updates a web search provider
Update(ctx context.Context, provider *types.WebSearchProviderEntity) error
// Delete deletes a web search provider (soft delete)
Delete(ctx context.Context, tenantID uint64, id string) error
// ClearDefault clears the default flag for all providers of a tenant, optionally excluding one
ClearDefault(ctx context.Context, tenantID uint64, excludeID string) error
}
WebSearchProviderRepository defines the repository interface for web search provider CRUD
type WebSearchProviderService ¶
type WebSearchProviderService interface {
// CreateProvider creates a new web search provider.
// provider.TenantID must be set by the caller (handler).
CreateProvider(ctx context.Context, provider *types.WebSearchProviderEntity) error
// UpdateProvider updates an existing provider.
// provider.TenantID must be set by the caller (handler) for the repository WHERE clause.
UpdateProvider(ctx context.Context, provider *types.WebSearchProviderEntity) error
// DeleteProvider deletes a provider by tenant + id.
DeleteProvider(ctx context.Context, tenantID uint64, id string) error
// UpdateProviderCredentials writes one or more credential fields.
// apiKey nil means "do not touch"; empty string is a no-op (clearing
// goes through ClearProviderCredential). Returns the updated entity.
UpdateProviderCredentials(
ctx context.Context, tenantID uint64, id string, apiKey *string,
) (*types.WebSearchProviderEntity, error)
// ClearProviderCredential removes a single credential field. Currently
// only "api_key" is recognized. Idempotent on already-empty fields.
ClearProviderCredential(ctx context.Context, tenantID uint64, id, field string) error
}
WebSearchProviderService defines the service interface for web search provider management. Tenant isolation is enforced by the handler layer (getOwned pattern). Service methods operate on entities whose TenantID is already verified.
type WebSearchService ¶
type WebSearchService interface {
// Search performs a web search using the provider entity identified by providerID.
// If providerID is empty, it falls back to the deprecated config.Provider field for backward compatibility.
Search(ctx context.Context, providerID string, config *types.WebSearchConfig, query string) ([]*types.WebSearchResult, error)
// CompressWithRAG performs RAG-based compression using a temporary, hidden knowledge base
// The temporary knowledge base is deleted after use. The UI will not list it due to repo filtering.
CompressWithRAG(ctx context.Context, sessionID string, tempKBID string, questions []string,
webSearchResults []*types.WebSearchResult, cfg *types.WebSearchConfig,
kbSvc KnowledgeBaseService, knowSvc KnowledgeService,
seenURLs map[string]bool, knowledgeIDs []string,
) (compressed []*types.WebSearchResult, kbID string, newSeen map[string]bool, newIDs []string, err error)
}
WebSearchService defines the interface for web search services
type WebSearchStateService ¶
type WebSearchStateService interface {
// GetWebSearchTempKBState retrieves the temporary KB state for web search from Redis
GetWebSearchTempKBState(
ctx context.Context,
sessionID string,
) (tempKBID string, seenURLs map[string]bool, knowledgeIDs []string)
// SaveWebSearchTempKBState saves the temporary KB state for web search to Redis
SaveWebSearchTempKBState(
ctx context.Context,
sessionID string,
tempKBID string,
seenURLs map[string]bool,
knowledgeIDs []string,
)
// DeleteWebSearchTempKBState deletes the temporary KB state for web search from Redis
DeleteWebSearchTempKBState(ctx context.Context, sessionID string) error
}
WebSearchStateService defines the service interface for managing web search temporary KB state
type WikiPageRepository ¶
type WikiPageRepository interface {
// Create inserts a new wiki page record.
Create(ctx context.Context, page *types.WikiPage) error
// Update rewrites a wiki page record with optimistic locking and
// unconditionally increments `version`. Callers are responsible for
// deciding whether the edit is user-visible — the service layer uses
// UpdateMeta for bookkeeping-only writes instead.
Update(ctx context.Context, page *types.WikiPage) error
// UpdateMeta updates bookkeeping / provenance fields (in/out links,
// status, source_refs, chunk_refs, page_metadata, updated_at) without
// touching `version`. Safe for link maintenance, re-ingest with an
// unchanged body, and status-only transitions.
UpdateMeta(ctx context.Context, page *types.WikiPage) error
// UpdateAutoLinkedContent rewrites `content`, `out_links` and
// `updated_at` in place while leaving `version` untouched. Intended for
// machine-only link markup changes (cross-link injection / dead-link
// cleanup) so the first-ingest page doesn't jump to v2 just because the
// post-processor added a `[[...]]` wrapper.
UpdateAutoLinkedContent(ctx context.Context, page *types.WikiPage) error
// GetByID retrieves a wiki page by its unique ID.
GetByID(ctx context.Context, id string) (*types.WikiPage, error)
// GetBySlug retrieves a wiki page by slug within a knowledge base.
GetBySlug(ctx context.Context, kbID string, slug string) (*types.WikiPage, error)
// List retrieves wiki pages with filtering and pagination.
List(ctx context.Context, req *types.WikiPageListRequest) ([]*types.WikiPage, int64, error)
// ListByType retrieves all wiki pages of a given type within a knowledge base.
ListByType(ctx context.Context, kbID string, pageType string) ([]*types.WikiPage, error)
// ListByTypeLight returns a paginated window of lightweight entries
// (slug/title/summary only) for the given page_type plus the total
// non-archived count. Used by the structured index API so reads do not
// have to materialize TEXT content for every wiki_pages row.
ListByTypeLight(ctx context.Context, kbID string, pageType string, limit int, offset int) ([]types.WikiIndexEntry, int64, error)
// ListBySourceRef retrieves all wiki pages that reference a given source knowledge ID.
ListBySourceRef(ctx context.Context, kbID string, sourceKnowledgeID string) ([]*types.WikiPage, error)
// ListSlugsBySourceRef returns just the slugs of pages that reference
// the given knowledge id. Same predicate as ListBySourceRef but
// projected to a single column for the wiki ingest pipeline's
// "before" snapshot path.
ListSlugsBySourceRef(ctx context.Context, kbID string, sourceKnowledgeID string) ([]string, error)
// ListBySlugs returns lightweight projections (slug/title/page_type/
// status/aliases/out_links) for the given slugs in one IN query.
// Used by the lazy fetcher path in wiki ingest's Map/Reduce phases.
ListBySlugs(ctx context.Context, kbID string, slugs []string) (map[string]*types.WikiPageLite, error)
// ListSummariesByKnowledgeIDs returns summary-page content keyed by
// the knowledge id that authored it. Used by the retract branch of
// reduceSlugUpdates for "what was this doc's contribution?" framing.
ListSummariesByKnowledgeIDs(ctx context.Context, kbID string, kids []string) (map[string]string, error)
// ExistsSlugs reports which of the given slugs are live (non-
// archived). Used by cleanDeadLinks to validate out-link targets
// without loading the referenced pages' content.
ExistsSlugs(ctx context.Context, kbID string, slugs []string) (map[string]bool, error)
// ListAllSlugs returns every non-archived slug in the KB. Used by
// lint to compute the live-slug set without paying for ListAll's
// full row materialization.
ListAllSlugs(ctx context.Context, kbID string) ([]string, error)
// ListPagesCursor returns up to `limit` pages ordered by id ASC,
// paginated by an opaque numeric cursor. Used by lint to stream
// the KB without loading every page at once.
ListPagesCursor(ctx context.Context, kbID string, cursor string, limit int) ([]*types.WikiPage, string, error)
// ListByTypeRecent returns the most-recently-updated pages of the
// given type, projected to slug/title/summary, capped at `limit`.
// Used by rebuildIndexPage's first-time generation path so the
// LLM prompt size is bounded on large KBs.
ListByTypeRecent(ctx context.Context, kbID string, pageType string, limit int) ([]types.WikiIndexEntry, error)
// FindSimilarPages returns the top-k pages whose lowercase title
// is most similar to the query under pg_trgm. `pageTypes` empty
// defaults to entity+concept. Used by the dedup pre-filter to
// surface candidate merge targets server-side.
FindSimilarPages(ctx context.Context, kbID string, query string, pageTypes []string, limit int) ([]*types.WikiPageLite, error)
// FindPagesByNormalizedTitle returns non-archived pages of pageType whose
// whitespace-stripped, lowercased title equals identity.
FindPagesByNormalizedTitle(ctx context.Context, kbID, pageType, identity string) ([]*types.WikiPageLite, error)
// FindPagesByNormalizedTitles is the batched form of
// FindPagesByNormalizedTitle.
FindPagesByNormalizedTitles(ctx context.Context, kbID, pageType string, identities []string) ([]*types.WikiPageLite, error)
// ListDistinctCategoryPaths returns the materialized paths of existing
// wiki folders (split into segments), capped at maxPaths. Used by the
// wiki ingest taxonomy planner as the pool of folders to reuse.
ListDistinctCategoryPaths(ctx context.Context, kbID string, maxPaths int) ([][]string, error)
// CreateFolder inserts a new directory node.
CreateFolder(ctx context.Context, folder *types.WikiFolder) error
// GetFolderByID retrieves a folder by id within a knowledge base.
GetFolderByID(ctx context.Context, kbID string, id string) (*types.WikiFolder, error)
// GetChildFolderByName returns the live child folder of parentID with the
// given name, or ErrWikiFolderNotFound. Used by find-or-create.
GetChildFolderByName(ctx context.Context, kbID string, parentID string, name string) (*types.WikiFolder, error)
// ListChildFolders returns the direct child folders of parentID, ordered
// by sort_order then name.
ListChildFolders(ctx context.Context, kbID string, parentID string) ([]*types.WikiFolder, error)
// ListAllFolders returns every folder in the knowledge base.
ListAllFolders(ctx context.Context, kbID string) ([]*types.WikiFolder, error)
// UpdateFolder rewrites a folder's mutable fields (name, parent_id, path,
// depth, sort_order, updated_at).
UpdateFolder(ctx context.Context, folder *types.WikiFolder) error
// DeleteFolder soft-deletes a folder by id.
DeleteFolder(ctx context.Context, kbID string, id string) error
// CountPagesInFolder returns the number of live (non-archived) pages
// directly in the folder (folder_id = id).
CountPagesInFolder(ctx context.Context, kbID string, folderID string) (int64, error)
// CountPagesByFolder returns live (non-archived) page counts grouped by
// folder_id. When pageTypes is non-empty the count is restricted to those
// page types; otherwise all types are counted. Pages at the wiki root carry
// the empty-string key.
CountPagesByFolder(ctx context.Context, kbID string, pageTypes []string) (map[string]int64, error)
// ListPagesByFolderIDs returns all pages whose folder_id is in the set.
// Used to recompute cached paths when a folder subtree is moved/renamed.
ListPagesByFolderIDs(ctx context.Context, kbID string, folderIDs []string) ([]*types.WikiPage, error)
// ListAll retrieves all non-archived wiki pages in a knowledge base (for link rebuilding, graph generation).
ListAll(ctx context.Context, kbID string) ([]*types.WikiPage, error)
// ListRecentForSuggestions returns recent user-visible wiki pages under the given
// knowledge bases, used to produce fallback suggested questions for Wiki-only KBs
// that do not have AI-generated document questions or recommended FAQ entries.
// Excludes the index page and archived pages. Returns up to `limit` rows sorted
// by updated_at descending.
ListRecentForSuggestions(ctx context.Context, tenantID uint64, kbIDs []string, limit int) ([]*types.WikiPage, error)
// Delete soft-deletes a wiki page by knowledge base ID and slug.
Delete(ctx context.Context, kbID string, slug string) error
// DeleteByID soft-deletes a wiki page by ID.
DeleteByID(ctx context.Context, id string) error
// Search performs full-text search on wiki pages within a knowledge base.
Search(ctx context.Context, kbID string, query string, limit int) ([]*types.WikiPage, error)
// SearchAcross performs the same full-text search across multiple
// knowledge bases, then returns a global top-N by match_rank.
SearchAcross(ctx context.Context, kbIDs []string, query string, limit int) ([]*types.WikiPage, error)
// CountByType returns page counts grouped by type for a knowledge base.
CountByType(ctx context.Context, kbID string) (map[string]int64, error)
// CountOrphans returns the number of pages with no inbound links.
CountOrphans(ctx context.Context, kbID string) (int64, error)
// UpdateWithRevision snapshots the version being superseded and applies
// the page update in a single transaction, so history can never contain
// a snapshot of a version that is still current. Inserting an already-
// present (page_id, version) pair is a silent no-op.
UpdateWithRevision(ctx context.Context, page *types.WikiPage, rev *types.WikiPageRevision) error
// ListRevisions returns snapshots for a page newest-first (content
// column omitted) plus the total snapshot count.
ListRevisions(ctx context.Context, kbID string, pageID string, limit int, offset int) ([]*types.WikiPageRevision, int64, error)
// GetRevision returns one snapshot with content.
GetRevision(ctx context.Context, kbID string, pageID string, version int) (*types.WikiPageRevision, error)
// PruneRevisions bounds per-page history storage using the two-tier
// retention described by the request.
PruneRevisions(ctx context.Context, req types.WikiRevisionPruneRequest) error
// DeleteRevisionsByPage hard-deletes a page's entire snapshot history.
DeleteRevisionsByPage(ctx context.Context, pageID string) error
// DeleteByKnowledgeBaseID soft-deletes all wiki pages for tenantID+kbID.
// Used by KB delete cleanup to batch-soft-delete every page without walking
// the folder tree. Bypasses the per-page chunk/link reconciliation that
// DeletePage does — the whole KB is going away, so cross-link cleanup is
// the KB delete flow's responsibility. tenantID is required so a tampered
// delete payload cannot wipe another tenant's wiki by kbID alone.
DeleteByKnowledgeBaseID(ctx context.Context, tenantID uint64, kbID string) error
// DeleteFoldersByKnowledgeBaseID soft-deletes all wiki folders for
// tenantID+kbID. Unlike DeleteFolder this does NOT enforce the emptiness
// guard — the KB is being deleted, so non-empty folders must go too.
DeleteFoldersByKnowledgeBaseID(ctx context.Context, tenantID uint64, kbID string) error
// DeleteRevisionsByKnowledgeBaseID hard-deletes all wiki page revisions
// for tenantID+kbID. Revisions have no deleted_at column (they are
// immutable snapshots, not soft-deletable rows), so this is a physical
// DELETE — same semantics as DeleteRevisionsByPage but scoped to the
// whole KB.
DeleteRevisionsByKnowledgeBaseID(ctx context.Context, tenantID uint64, kbID string) error
// DeleteIssuesByKnowledgeBaseID soft-deletes all wiki page issues for
// tenantID+kbID. Issues reference the KB and must be cleaned up
// alongside pages to avoid orphans.
DeleteIssuesByKnowledgeBaseID(ctx context.Context, tenantID uint64, kbID string) error
// CreateIssue inserts a new wiki page issue record.
CreateIssue(ctx context.Context, issue *types.WikiPageIssue) error
// ListIssues retrieves issues with optional filtering by slug and status.
ListIssues(ctx context.Context, kbID string, slug string, status string) ([]*types.WikiPageIssue, error)
// UpdateIssueStatus updates the status of an issue of kbID; ErrWikiIssueNotFound
// when no such issue belongs to it.
UpdateIssueStatus(ctx context.Context, kbID string, issueID string, status string) error
}
WikiPageRepository defines the wiki page data persistence interface.
type WikiPageService ¶
type WikiPageService interface {
// CreatePage creates a new wiki page, parses outbound links, updates
// bidirectional link references, and syncs to chunks for retrieval.
CreatePage(ctx context.Context, page *types.WikiPage) (*types.WikiPage, error)
// UpdatePage updates an existing wiki page, re-parses links, and updates
// bidirectional references. The `version` field is incremented only when
// a user-visible content field (title, content, summary, page_type,
// status) actually differs from the stored value — bookkeeping-only
// writes (e.g. refreshing source_refs after a same-content re-ingest) are
// persisted without bumping the version.
UpdatePage(ctx context.Context, page *types.WikiPage) (*types.WikiPage, error)
// UpdatePageMeta updates only metadata (status, source_refs, etc.) without
// incrementing version or re-parsing links. Use for publish/archive/source
// ref changes driven by internal reconciliation.
UpdatePageMeta(ctx context.Context, page *types.WikiPage) error
// UpdateAutoLinkedContent persists content changes that come from
// machine-only link decoration — cross-link injection and dead-link
// cleanup — without bumping `version`. Out-links are re-parsed from the
// new body and bidirectional in-link references on target pages are
// refreshed. Conceptually the body still represents the same revision
// the user saw before, just with wiki-link markup added or stripped, so
// surfacing a version bump to end users is misleading.
UpdateAutoLinkedContent(ctx context.Context, page *types.WikiPage) error
// GetPageBySlug retrieves a wiki page by its slug within a knowledge base.
GetPageBySlug(ctx context.Context, kbID string, slug string) (*types.WikiPage, error)
// RepairContentLinks rewrites dead [[slug]] references in content to their
// most likely live target (rewrite-only — never strips). Used by the agent
// write path to auto-correct LLM-mangled slugs (especially UUID-based
// summary slugs) before persistence. Returns the possibly-updated content
// and whether any rewrite happened. Best-effort: callers may ignore errors.
RepairContentLinks(ctx context.Context, kbID, selfSlug, content string) (string, bool, error)
// GetPageByID retrieves a wiki page by its unique ID.
GetPageByID(ctx context.Context, id string) (*types.WikiPage, error)
// ListPages lists wiki pages with optional filtering and pagination.
ListPages(ctx context.Context, req *types.WikiPageListRequest) (*types.WikiPageListResponse, error)
// DeletePage soft-deletes a wiki page and removes its chunk sync.
DeletePage(ctx context.Context, kbID string, slug string) error
// GetIndex returns the index page for a knowledge base.
// Creates a default one if it doesn't exist.
//
// The index row now carries only the LLM-generated intro in its content
// column — the heavy directory listing was moved out of wiki_pages.
// Callers that want a renderable directory should use GetIndexView,
// which assembles {intro, groups} structured output on demand.
GetIndex(ctx context.Context, kbID string) (*types.WikiPage, error)
// GetIndexView returns the structured index response — intro (from the
// index row) plus a paginated window of directory entries per requested
// page_type. `types` restricts which page types to include (empty =
// all), `limit` bounds the per-type window size, `cursor` is an opaque
// offset string resumed from a previous response.
//
// Keeping the directory as JSON rather than a multi-MB markdown blob
// means a 40k-page KB no longer pays O(N) transport + rendering cost
// on every index open. See /wiki/index handler + WikiBrowser.vue for
// the consumer.
GetIndexView(ctx context.Context, kbID string, pageTypes []string, limit int, cursor string) (*types.WikiIndexResponse, error)
// GetGraph returns the link graph data for visualization. The caller
// supplies a WikiGraphRequest describing the desired slice of the graph
// (overview top-N or ego neighborhood around a center slug). Callers
// that need the full graph (e.g. wiki lint) can set Limit <= 0 to
// disable the node cap; the HTTP handler always clamps Limit to a
// safe range before invoking the service.
GetGraph(ctx context.Context, req *types.WikiGraphRequest) (*types.WikiGraphData, error)
// GetStats returns aggregate statistics about the wiki.
GetStats(ctx context.Context, kbID string) (*types.WikiStats, error)
// RebuildLinks re-parses all pages and rebuilds bidirectional link references.
// It is best-effort: pages that could not be written are skipped, and any
// such failure is returned as a joined error counting the failed pages.
RebuildLinks(ctx context.Context, kbID string) error
// InjectCrossLinks scans specified pages and injects [[wiki-links]] for mentions
// of other wiki page titles/aliases in the content.
InjectCrossLinks(ctx context.Context, kbID string, affectedSlugs []string)
// RebuildIndexPage regenerates the index page.
RebuildIndexPage(ctx context.Context, kbID string) error
// ListAllPages retrieves all non-archived wiki pages in a knowledge base without pagination.
// Used for index rebuild, graph generation, cross-link injection, etc.
ListAllPages(ctx context.Context, kbID string) ([]*types.WikiPage, error)
// ListByType returns every wiki page of the given type. Used by
// callers that need the full set for a specific type (e.g. intro
// regeneration needs summary pages). For directory rendering, use
// GetIndexView instead — it paginates via ListByTypeLight.
ListByType(ctx context.Context, kbID string, pageType string) ([]*types.WikiPage, error)
// ListPagesBySourceRef retrieves all wiki pages whose source_refs reference
// the given knowledge ID. Used by delete/ingest reconciliation paths that
// need to find pages touched by a specific document at read time (rather
// than relying on a caller-provided stale snapshot).
ListPagesBySourceRef(ctx context.Context, kbID string, knowledgeID string) ([]*types.WikiPage, error)
// ListSlugsBySourceRef returns just the slugs of pages that cite the
// given knowledge id. Cheaper than ListPagesBySourceRef when callers
// only need the slug set (e.g. wiki ingest's "before" snapshot).
ListSlugsBySourceRef(ctx context.Context, kbID string, knowledgeID string) ([]string, error)
// ListBySlugs is the lazy fetcher used by the wiki ingest batch
// context: returns lightweight projections (no content / source_refs)
// for the requested slugs in a single IN query. Used in place of
// the pre-batch ListAllPages dump.
ListBySlugs(ctx context.Context, kbID string, slugs []string) (map[string]*types.WikiPageLite, error)
// ListSummariesByKnowledgeIDs returns summary-page content keyed by
// the knowledge id that authored it. Used by the retract / reparse
// branches of reduceSlugUpdates for "what was this doc's
// contribution?" framing.
ListSummariesByKnowledgeIDs(ctx context.Context, kbID string, kids []string) (map[string]string, error)
// ExistsSlugs reports which of the given slugs are live (non-archived).
// Used by cleanDeadLinks to validate out-link targets cheaply.
ExistsSlugs(ctx context.Context, kbID string, slugs []string) (map[string]bool, error)
// ListAllSlugs returns every non-archived slug in the KB. Used by
// lint to compute the live-slug set for broken-link detection.
ListAllSlugs(ctx context.Context, kbID string) ([]string, error)
// ListPagesCursor walks the KB in id-asc order with an opaque
// cursor. Used by lint to stream the page set without holding it
// all in memory at once.
ListPagesCursor(ctx context.Context, kbID string, cursor string, limit int) ([]*types.WikiPage, string, error)
// ListByTypeRecent returns the most-recently-updated pages of the
// given type, projected to slug/title/summary, capped at `limit`.
// Used by rebuildIndexPage's first-time generation path.
ListByTypeRecent(ctx context.Context, kbID string, pageType string, limit int) ([]types.WikiIndexEntry, error)
// FindSimilarPages performs a pg_trgm similarity search over
// page titles. Used by the dedup pre-filter to surface candidate
// merge targets server-side.
FindSimilarPages(ctx context.Context, kbID string, query string, pageTypes []string, limit int) ([]*types.WikiPageLite, error)
// FindPagesByNormalizedTitle returns non-archived pages of pageType whose
// display title matches identity after the same whitespace/case fold used
// by wiki ingest identity claims. Used so exact same-title pages are found
// even when they miss the trigram top-K.
FindPagesByNormalizedTitle(ctx context.Context, kbID, pageType, identity string) ([]*types.WikiPageLite, error)
// FindPagesByNormalizedTitles is the batched form of
// FindPagesByNormalizedTitle. identities are already whitespace-stripped
// and lowercased; empty entries are ignored.
FindPagesByNormalizedTitles(ctx context.Context, kbID, pageType string, identities []string) ([]*types.WikiPageLite, error)
// ListDistinctCategoryPaths returns the existing wiki folder paths (split
// into segments), capped at maxPaths. Used by wiki ingest's taxonomy
// planner as the pool of folders to reuse.
ListDistinctCategoryPaths(ctx context.Context, kbID string, maxPaths int) ([][]string, error)
// ListChildFolders returns the direct child folders of parentID ("" =
// root) relevant to the given page types, each enriched with a recursive
// page count (the folder's whole subtree, restricted to pageTypes) and
// whether it has relevant sub-folders. A folder is shown when its subtree
// holds at least one page of pageTypes, or when it is entirely empty (a
// user-created container with no pages of any type). When pageTypes is
// empty every type counts.
ListChildFolders(
ctx context.Context, kbID string, parentID string, pageTypes []string,
) ([]types.WikiFolderNode, error)
// GetFolder retrieves a single folder by id.
GetFolder(ctx context.Context, kbID string, id string) (*types.WikiFolder, error)
// CreateFolder creates a new (initially empty) folder under parentID and
// returns it. Fails if a sibling with the same name already exists.
CreateFolder(ctx context.Context, kbID string, tenantID uint64, parentID string, name string) (*types.WikiFolder, error)
// RenameOrMoveFolder renames and/or reparents a folder, recomputing the
// materialized path/depth of the whole subtree and the cached paths of
// every page underneath. Returns the updated folder.
RenameOrMoveFolder(ctx context.Context, kbID string, id string, newName string, newParentID string, moveParent bool) (*types.WikiFolder, error)
// DeleteFolder removes an empty folder. Fails if it still contains pages
// or child folders (the UI must move or delete contents first).
DeleteFolder(ctx context.Context, kbID string, id string) error
// PruneEmptyFolderChains deletes candidate folders that are still empty,
// walking upward through newly-empty ancestors. Candidates are supplied by
// document retract cleanup so unrelated user-created empty folders remain.
PruneEmptyFolderChains(ctx context.Context, kbID string, folderIDs []string) ([]string, error)
// FindOrCreateFolderPath resolves a category path (e.g. ["AI","RAG"]) to a
// folder id, creating any missing intermediate folders. Returns the leaf
// folder id and the canonical (cleaned) path. An empty/blank path resolves
// to the root ("").
FindOrCreateFolderPath(ctx context.Context, kbID string, tenantID uint64, path []string) (string, []string, error)
// MovePage relocates a page into folderID ("" = root), recomputing its
// cached category path. Returns the updated page.
MovePage(ctx context.Context, kbID string, slug string, folderID string) (*types.WikiPage, error)
// CountByType returns page counts grouped by type for a knowledge
// base. Re-exposed at the service layer so the index intro
// generation path can frame the LLM prompt with "showing N of M"
// metadata when the recent-N projection doesn't cover the full
// summary set.
CountByType(ctx context.Context, kbID string) (map[string]int64, error)
// SearchPages performs full-text search over wiki pages.
SearchPages(ctx context.Context, kbID string, query string, limit int) ([]*types.WikiPage, error)
// SearchPagesAcross searches wiki pages across multiple knowledge bases
// with the same ranking as SearchPages, then returns a global top-N.
SearchPagesAcross(ctx context.Context, kbIDs []string, query string, limit int) ([]*types.WikiPage, error)
// ListRevisions returns the stored historical snapshots for a page
// (newest first, content omitted) plus the total snapshot count and the
// page's current version. The current version itself has no revision
// row — it lives in wiki_pages.
ListRevisions(ctx context.Context, kbID string, slug string, limit int, offset int) (*types.WikiPageRevisionListResponse, error)
// GetRevision returns one historical snapshot (content included).
GetRevision(ctx context.Context, kbID string, slug string, version int) (*types.WikiPageRevision, error)
// RevertPageToVersion rolls the page back to the content of the given
// stored revision by applying it as a regular edit: the pre-revert state
// is snapshotted, version advances, and links are re-parsed. The edit is
// attributed to WikiEditSourceRevert.
RevertPageToVersion(ctx context.Context, kbID string, slug string, version int) (*types.WikiPage, error)
// CreateIssue logs a new issue for a wiki page.
CreateIssue(ctx context.Context, issue *types.WikiPageIssue) (*types.WikiPageIssue, error)
// ListIssues retrieves issues for a knowledge base, optionally filtered by slug and status.
ListIssues(ctx context.Context, kbID string, slug string, status string) ([]*types.WikiPageIssue, error)
// UpdateIssueStatus updates the status of an issue of kbID (e.g. pending -> resolved/ignored).
UpdateIssueStatus(ctx context.Context, kbID string, issueID string, status string) error
}
WikiPageService defines the wiki page service interface. Provides high-level operations for wiki page CRUD, link management, and chunk synchronization.
Source Files
¶
- agent.go
- attachment.go
- audit_log.go
- chunk.go
- custom_agent.go
- datasource.go
- document_parser.go
- embed_channel.go
- evaluation.go
- file.go
- knowledge.go
- knowledgebase.go
- knowledgebase_profile.go
- mcp_endpoint.go
- mcp_metadata.go
- mcp_oauth.go
- mcp_service.go
- mcp_tool_approval.go
- memory.go
- message.go
- message_suggestion.go
- model.go
- organization.go
- resource.go
- retriever.go
- retriever_graph.go
- session.go
- storagebackend.go
- stream_manager.go
- system_setting.go
- tag.go
- task_enqueuer.go
- task_handler.go
- task_inspector.go
- task_queue.go
- temporary_document.go
- tenant.go
- tenant_invitation.go
- tenant_invitation_service.go
- tenant_member.go
- tenant_member_service.go
- user.go
- user_resource_favorite.go
- vectorstore.go
- web_search.go
- web_search_provider.go
- web_search_state.go
- weknoracloud.go
- wiki_page.go