Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
574 lines
28 KiB
Go
574 lines
28 KiB
Go
package types
|
||
|
||
// Worker-pool names are part of the runtime observability API. Each pool is
|
||
// backed by an independent asynq.Server, so concurrency is hard-isolated
|
||
// between pools instead of being only a weighted dequeue preference.
|
||
const (
|
||
WorkerPoolCore = "core"
|
||
WorkerPoolPostProcess = "postprocess"
|
||
WorkerPoolEnrichment = "enrichment"
|
||
WorkerPoolMaintenance = "maintenance"
|
||
WorkerPoolShared = "shared"
|
||
WorkerPoolWiki = "wiki"
|
||
|
||
// Upstream defaults are explicit guarantees plus an elastic pool. The
|
||
// shared pool may consume core and enrichment queues, so idle capacity in
|
||
// either stage can be borrowed without sacrificing the dedicated minimums.
|
||
DefaultCoreWorkerConcurrency = 8
|
||
DefaultPostProcessWorkerConcurrency = 2
|
||
DefaultEnrichmentWorkerConcurrency = 12
|
||
DefaultMaintenanceWorkerConcurrency = 4
|
||
DefaultSharedWorkerConcurrency = 6
|
||
DefaultWikiWorkerConcurrency = 8
|
||
DefaultUpstreamWorkerConcurrency = DefaultCoreWorkerConcurrency +
|
||
DefaultPostProcessWorkerConcurrency + DefaultEnrichmentWorkerConcurrency +
|
||
DefaultMaintenanceWorkerConcurrency + DefaultSharedWorkerConcurrency
|
||
)
|
||
|
||
// Asynq queue names. QueueMaintenance intentionally keeps the physical Redis
|
||
// name "low" so tasks enqueued by older releases remain consumable during a
|
||
// rolling deployment. New code uses the business-semantic constant.
|
||
const (
|
||
QueueDefault = "default"
|
||
// QueueChatAttachment carries session-scoped chat attachment parsing. It
|
||
// lives in the core pool but with a higher weight than QueueDefault so
|
||
// interactive chat uploads are not starved by knowledge-base batch imports.
|
||
QueueChatAttachment = "chat_attachment"
|
||
QueuePostProcess = "postprocess"
|
||
QueueSummary = "summary"
|
||
QueueMultimodal = "multimodal"
|
||
QueueGraph = "graph"
|
||
QueueQuestion = "question"
|
||
QueueSync = "sync"
|
||
QueueMaintenance = "low"
|
||
QueueWiki = "wiki"
|
||
// QueueMemory carries debounced long-term memory distillation. It sits in
|
||
// the enrichment pool because it is a background LLM call whose latency
|
||
// nobody is waiting on.
|
||
QueueMemory = "memory"
|
||
)
|
||
|
||
// QueueDefinition is the single source of truth for queue topology. Worker
|
||
// servers and runtime inspection both consume this registry, preventing the
|
||
// scheduling weights shown to operators from drifting from the actual server
|
||
// configuration.
|
||
type QueueDefinition struct {
|
||
Name string
|
||
Pool string
|
||
Weight int
|
||
SharedWeight int
|
||
TaskTypes []string
|
||
}
|
||
|
||
var queueDefinitions = []QueueDefinition{
|
||
{Name: QueueDefault, Pool: WorkerPoolCore, Weight: 1, SharedWeight: 3, TaskTypes: []string{
|
||
TypeDocumentProcess, TypeManualProcess,
|
||
}},
|
||
// Interactive chat attachment parsing: higher core weight than the default
|
||
// queue so a large KB import cannot make chat uploads queue behind it.
|
||
{Name: QueueChatAttachment, Pool: WorkerPoolCore, Weight: 3, SharedWeight: 3, TaskTypes: []string{
|
||
TypeTemporaryDocumentProcess,
|
||
}},
|
||
{Name: QueuePostProcess, Pool: WorkerPoolPostProcess, Weight: 1, TaskTypes: []string{
|
||
TypeKnowledgePostProcess,
|
||
}},
|
||
{Name: QueueSummary, Pool: WorkerPoolEnrichment, Weight: 2, SharedWeight: 2, TaskTypes: []string{
|
||
TypeSummaryGeneration, TypeDataTableSummary, TypeKnowledgeAutoTag,
|
||
}},
|
||
{Name: QueueMultimodal, Pool: WorkerPoolEnrichment, Weight: 1, SharedWeight: 1, TaskTypes: []string{TypeImageMultimodal}},
|
||
{Name: QueueGraph, Pool: WorkerPoolEnrichment, Weight: 1, SharedWeight: 1, TaskTypes: []string{TypeChunkExtract}},
|
||
{Name: QueueQuestion, Pool: WorkerPoolEnrichment, Weight: 1, SharedWeight: 1, TaskTypes: []string{TypeQuestionGeneration}},
|
||
{Name: QueueMemory, Pool: WorkerPoolEnrichment, Weight: 1, SharedWeight: 1, TaskTypes: []string{TypeMemoryExtract}},
|
||
{Name: QueueSync, Pool: WorkerPoolMaintenance, Weight: 2, TaskTypes: []string{TypeDataSourceSync}},
|
||
{Name: QueueMaintenance, Pool: WorkerPoolMaintenance, Weight: 1, TaskTypes: []string{
|
||
TypeFAQImport, TypeKBClone, TypeIndexDelete, TypeKBDelete,
|
||
TypeKnowledgeListDelete, TypeKnowledgeListReparse, TypeKnowledgeMove,
|
||
}},
|
||
{Name: QueueWiki, Pool: WorkerPoolWiki, Weight: 1, TaskTypes: []string{TypeWikiIngest, TypeWikiFinalize}},
|
||
}
|
||
|
||
// QueueDefinitions returns a copy so callers cannot mutate global topology.
|
||
func QueueDefinitions() []QueueDefinition {
|
||
definitions := make([]QueueDefinition, len(queueDefinitions))
|
||
for i, definition := range queueDefinitions {
|
||
definitions[i] = definition
|
||
definitions[i].TaskTypes = append([]string(nil), definition.TaskTypes...)
|
||
}
|
||
return definitions
|
||
}
|
||
|
||
// QueueForTaskType returns the declared queue for a task type. Producers still
|
||
// pass the queue explicitly to asynq, while tests and observability can use
|
||
// this mapping to detect drift.
|
||
func QueueForTaskType(taskType string) (string, bool) {
|
||
for _, definition := range queueDefinitions {
|
||
for _, declaredType := range definition.TaskTypes {
|
||
if declaredType == taskType {
|
||
return definition.Name, true
|
||
}
|
||
}
|
||
}
|
||
return "", false
|
||
}
|
||
|
||
// QueueWeightsForPool returns the asynq queue configuration for one worker
|
||
// pool. An empty map indicates a programming error in the pool declaration.
|
||
func QueueWeightsForPool(pool string) map[string]int {
|
||
weights := make(map[string]int)
|
||
for _, definition := range queueDefinitions {
|
||
if definition.Pool == pool {
|
||
weights[definition.Name] = definition.Weight
|
||
}
|
||
}
|
||
return weights
|
||
}
|
||
|
||
// QueueWeightsForSharedPool returns the core/enrichment queues eligible for
|
||
// elastic capacity. Post-process and maintenance are deliberately excluded:
|
||
// post-process needs a small latency guarantee, while long maintenance tasks
|
||
// must not pin burst capacity intended for the user-facing pipeline.
|
||
func QueueWeightsForSharedPool() map[string]int {
|
||
weights := make(map[string]int)
|
||
for _, definition := range queueDefinitions {
|
||
if definition.SharedWeight > 0 {
|
||
weights[definition.Name] = definition.SharedWeight
|
||
}
|
||
}
|
||
return weights
|
||
}
|
||
|
||
// WorkerPoolConcurrency contains the explicit per-instance pool capacities.
|
||
// Unlike the old ratio allocator, each field is independently configurable.
|
||
type WorkerPoolConcurrency struct {
|
||
Core int
|
||
PostProcess int
|
||
Enrichment int
|
||
Maintenance int
|
||
Shared int
|
||
Wiki int
|
||
}
|
||
|
||
func DefaultWorkerPoolConcurrency() WorkerPoolConcurrency {
|
||
return WorkerPoolConcurrency{
|
||
Core: DefaultCoreWorkerConcurrency,
|
||
PostProcess: DefaultPostProcessWorkerConcurrency,
|
||
Enrichment: DefaultEnrichmentWorkerConcurrency,
|
||
Maintenance: DefaultMaintenanceWorkerConcurrency,
|
||
Shared: DefaultSharedWorkerConcurrency,
|
||
Wiki: DefaultWikiWorkerConcurrency,
|
||
}
|
||
}
|
||
|
||
// ResolveWorkerPoolConcurrency centralizes setting keys, environment names,
|
||
// defaults, and invalid-value fallback for both server construction and the
|
||
// runtime API. The callback lets this package stay independent of the system
|
||
// setting service interface.
|
||
func ResolveWorkerPoolConcurrency(read func(key, env string, fallback int) int) WorkerPoolConcurrency {
|
||
allocation := DefaultWorkerPoolConcurrency()
|
||
if read == nil {
|
||
return allocation
|
||
}
|
||
positive := func(key, env string, fallback int) int {
|
||
value := read(key, env, fallback)
|
||
if value < 1 {
|
||
return fallback
|
||
}
|
||
return value
|
||
}
|
||
allocation.Core = positive("asynq.core_concurrency", "WEKNORA_ASYNQ_CORE_CONCURRENCY", allocation.Core)
|
||
allocation.PostProcess = positive("asynq.postprocess_concurrency", "WEKNORA_ASYNQ_POSTPROCESS_CONCURRENCY", allocation.PostProcess)
|
||
allocation.Enrichment = positive("asynq.enrichment_concurrency", "WEKNORA_ASYNQ_ENRICHMENT_CONCURRENCY", allocation.Enrichment)
|
||
allocation.Maintenance = positive("asynq.maintenance_concurrency", "WEKNORA_ASYNQ_MAINTENANCE_CONCURRENCY", allocation.Maintenance)
|
||
allocation.Shared = positive("asynq.shared_concurrency", "WEKNORA_ASYNQ_SHARED_CONCURRENCY", allocation.Shared)
|
||
allocation.Wiki = positive("asynq.wiki_concurrency", "WEKNORA_WIKI_ASYNQ_CONCURRENCY", allocation.Wiki)
|
||
return allocation
|
||
}
|
||
|
||
func (c WorkerPoolConcurrency) UpstreamTotal() int {
|
||
return c.Core + c.PostProcess + c.Enrichment + c.Maintenance + c.Shared
|
||
}
|
||
|
||
// QueueStat is a read-only depth snapshot of a single asynq queue, used
|
||
// by the System Admin runtime dashboard. Field names mirror the counts
|
||
// exposed by asynq.QueueInfo. Pool / Weight are static metadata (which
|
||
// worker pool drains the queue and its scheduling weight within that
|
||
// pool) so the UI can group and explain the lanes without the frontend
|
||
// hard-coding the topology.
|
||
type QueueStat struct {
|
||
Name string `json:"name"`
|
||
// Pool is the independent worker pool that drains this queue.
|
||
Pool string `json:"pool"`
|
||
// Weight is the queue's scheduling weight inside its pool.
|
||
Weight int `json:"weight"`
|
||
// Size is the total number of tasks in the queue (pending + active +
|
||
// scheduled + retry + aggregating + archived).
|
||
Size int `json:"size"`
|
||
Pending int `json:"pending"`
|
||
Active int `json:"active"`
|
||
Scheduled int `json:"scheduled"`
|
||
Retry int `json:"retry"`
|
||
Archived int `json:"archived"`
|
||
Completed int `json:"completed"`
|
||
// Processed / Failed are today's counters (reset daily).
|
||
Processed int `json:"processed"`
|
||
Failed int `json:"failed"`
|
||
// Paused reports whether the queue is paused (tasks not consumed).
|
||
Paused bool `json:"paused"`
|
||
// LatencyMs is the age of the oldest pending task, in milliseconds.
|
||
LatencyMs int64 `json:"latency_ms"`
|
||
// MemoryUsageBytes is the approximate Redis memory the queue occupies.
|
||
MemoryUsageBytes int64 `json:"memory_usage_bytes"`
|
||
}
|
||
|
||
// WorkerServerStat is one live asynq server heartbeat. Queue weights identify
|
||
// which logical pool the server belongs to; the runtime handler aggregates
|
||
// these records across replicas so configured per-instance capacity is not
|
||
// confused with actual cluster capacity.
|
||
type WorkerServerStat struct {
|
||
Concurrency int
|
||
Active int
|
||
Status string
|
||
Queues map[string]int
|
||
}
|
||
|
||
const (
|
||
TypeChunkExtract = "chunk:extract"
|
||
TypeDocumentProcess = "document:process" // 文档处理任务
|
||
TypeFAQImport = "faq:import" // FAQ导入任务(包含dry run模式)
|
||
TypeQuestionGeneration = "question:generation" // 问题生成任务
|
||
TypeSummaryGeneration = "summary:generation" // 摘要生成任务
|
||
TypeKBClone = "kb:clone" // 知识库复制任务
|
||
TypeIndexDelete = "index:delete" // 索引删除任务
|
||
TypeKBDelete = "kb:delete" // 知识库删除任务
|
||
TypeKnowledgeListDelete = "knowledge:list_delete" // 批量删除知识任务
|
||
TypeKnowledgeListReparse = "knowledge:list_reparse" // 批量重解析知识任务
|
||
TypeKnowledgeMove = "knowledge:move" // 知识移动任务
|
||
TypeDataTableSummary = "datatable:summary" // 表格摘要任务
|
||
TypeImageMultimodal = "image:multimodal" // 图片多模态处理任务(OCR + VLM Caption)
|
||
TypeKnowledgePostProcess = "knowledge:post_process" // 知识后处理任务(统一调度)
|
||
TypeKnowledgeAutoTag = "knowledge:auto_tag" // 文档自动关联知识库已有标签
|
||
TypeManualProcess = "manual:process" // 手工知识更新任务(cleanup + 重新索引)
|
||
TypeDataSourceSync = "datasource:sync" // 数据源同步任务
|
||
TypeWikiIngest = "wiki:ingest" // Wiki 页面同步任务
|
||
TypeWikiFinalize = "wiki:finalize" // Wiki KB 级收尾任务(防抖:索引重建/死链清理/交叉链接)
|
||
TypeTemporaryDocumentProcess = "temporary_document:process" // 会话临时文档解析任务
|
||
// TypeMemoryExtract 长期记忆抽取任务(会话轮次防抖后异步执行)
|
||
TypeMemoryExtract = "memory:extract"
|
||
)
|
||
|
||
// MemoryExtractPayload carries everything the background distillation task
|
||
// needs. Scope (tenant + subject) travels in the payload rather than being
|
||
// read from the worker context: asynq and the Lite executor both start from a
|
||
// bare context, so anything the request knew and the payload does not carry is
|
||
// simply gone by the time the handler runs.
|
||
type MemoryExtractPayload struct {
|
||
TracingContext
|
||
AttemptID string `json:"attempt_id,omitempty"`
|
||
TenantID uint64 `json:"tenant_id"`
|
||
SubjectID string `json:"subject_id"`
|
||
SessionID string `json:"session_id"`
|
||
// MessageID is the assistant message that closed the triggering turn. It
|
||
// bounds the extraction window and is stored as the source of any item
|
||
// produced, so every memory can be traced back to a real message.
|
||
MessageID string `json:"message_id"`
|
||
// ChatModelID is the model the conversation itself used. The extraction
|
||
// task falls back to it when MemoryConfig.ExtractModelID is blank, which
|
||
// is what the settings UI promises.
|
||
ChatModelID string `json:"chat_model_id,omitempty"`
|
||
Language string `json:"language,omitempty"`
|
||
}
|
||
|
||
// ExtractChunkPayload represents the extract chunk task payload
|
||
type GraphSourceIdentity struct {
|
||
KnowledgeBaseID string `json:"knowledge_base_id"`
|
||
KnowledgeID string `json:"knowledge_id"`
|
||
ContentSHA256 string `json:"content_sha256"`
|
||
}
|
||
|
||
type ExtractChunkPayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
ChunkID string `json:"chunk_id"`
|
||
ModelID string `json:"model_id"`
|
||
// KnowledgeID + Attempt link the per-chunk extract back to the parent
|
||
// parse attempt's postprocess stage so the worker can record a
|
||
// postprocess.graph.chunk[i] subspan. 0 / "" means "skip span
|
||
// recording" for legacy in-flight tasks.
|
||
KnowledgeID string `json:"knowledge_id,omitempty"`
|
||
Attempt int `json:"attempt,omitempty"`
|
||
// ChunkIndex is the 0-based ordinal of this chunk inside the parent
|
||
// knowledge's text-chunk set, used as the subspan name suffix
|
||
// ("postprocess.graph.chunk[3]") so the timeline preserves order.
|
||
ChunkIndex int `json:"chunk_index,omitempty"`
|
||
// A reviewed backfill must not spend model budget on a changed source.
|
||
ExpectedSource *GraphSourceIdentity `json:"expected_source,omitempty"`
|
||
}
|
||
|
||
// DocumentProcessPayload represents the document process task payload
|
||
type DocumentProcessPayload struct {
|
||
TracingContext
|
||
RequestId string `json:"request_id"`
|
||
TenantID uint64 `json:"tenant_id"`
|
||
KnowledgeID string `json:"knowledge_id"`
|
||
KnowledgeBaseID string `json:"knowledge_base_id"`
|
||
FilePath string `json:"file_path,omitempty"` // 文件路径(文件导入时使用)
|
||
FileName string `json:"file_name,omitempty"` // 文件名(文件导入时使用)
|
||
FileType string `json:"file_type,omitempty"` // 文件类型(文件导入时使用)
|
||
URL string `json:"url,omitempty"` // URL(URL导入时使用)
|
||
FileURL string `json:"file_url,omitempty"` // 文件资源链接(file_url导入时使用)
|
||
Passages []string `json:"passages,omitempty"` // 文本段落(文本导入时使用)
|
||
EnableMultimodel bool `json:"enable_multimodel"`
|
||
EnableQuestionGeneration bool `json:"enable_question_generation"` // 是否启用问题生成
|
||
QuestionCount int `json:"question_count,omitempty"` // 每个chunk生成的问题数量
|
||
Language string `json:"language,omitempty"` // Request locale for {{language}} in prompt templates
|
||
// Attempt is the per-knowledge attempt number this task belongs to.
|
||
// Set on enqueue (initial parse → attempt 1; reparse → max+1) so
|
||
// every span recorded by this task lands on the right attempt
|
||
// branch. Asynq retries within an attempt keep the same value so
|
||
// retried spans overwrite the previous attempt's row rather than
|
||
// fan out into a new attempt for every retry.
|
||
Attempt int `json:"attempt,omitempty"`
|
||
}
|
||
|
||
// FAQImportPayload represents the FAQ import task payload (including dry run mode)
|
||
type FAQImportPayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
TaskID string `json:"task_id"`
|
||
KBID string `json:"kb_id"`
|
||
KnowledgeID string `json:"knowledge_id,omitempty"` // 仅非 dry run 模式需要
|
||
Entries []FAQEntryPayload `json:"entries,omitempty"` // 小数据量时直接存储在 payload 中
|
||
EntriesURL string `json:"entries_url,omitempty"` // 大数据量时存储到对象存储,这里存储 URL
|
||
EntryCount int `json:"entry_count,omitempty"` // 条目总数(使用 EntriesURL 时需要)
|
||
Mode string `json:"mode"`
|
||
DryRun bool `json:"dry_run"` // dry run 模式只验证不导入
|
||
EnqueuedAt int64 `json:"enqueued_at"` // 任务入队时间戳,用于区分同一 TaskID 的不同次提交
|
||
InstanceID string `json:"instance_id,omitempty"`
|
||
Initiator TaskInitiator `json:"initiator,omitempty"`
|
||
}
|
||
|
||
// QuestionGenerationPayload represents the question generation task payload
|
||
type QuestionGenerationPayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
KnowledgeBaseID string `json:"knowledge_base_id"`
|
||
KnowledgeID string `json:"knowledge_id"`
|
||
QuestionCount int `json:"question_count"`
|
||
// Language is the request locale (e.g. zh-CN, en-US) when the task was enqueued, used for {{language}} / {{lang}} in templates.
|
||
Language string `json:"language,omitempty"`
|
||
// Attempt links this task to the parent parse attempt so the worker
|
||
// can record a postprocess.question subspan under the right attempt's
|
||
// postprocess stage. 0 means "skip span recording" (legacy in-flight
|
||
// tasks queued before this field shipped, or callers without a
|
||
// tracker).
|
||
Attempt int `json:"attempt,omitempty"`
|
||
// ChunkIDs switches the handler into batched fan-out mode: the task
|
||
// generates questions for this ordered window of text chunks only.
|
||
// Batching (rather than one task per chunk) keeps the task count
|
||
// bounded for very large documents, while still giving each batch
|
||
// independent retry / cancellation / tracing and letting the worker
|
||
// do a single embedding BatchIndex per batch. Empty means the legacy
|
||
// whole-knowledge mode (kept for in-flight tasks queued before fan-out
|
||
// shipped), where the handler iterates all text chunks itself.
|
||
// Following the ExtractChunkPayload precedent, we carry only chunk ids
|
||
// (not their content) so the payload stays small and the worker reads
|
||
// fresh content at run time.
|
||
ChunkIDs []string `json:"chunk_ids,omitempty"`
|
||
// ChunkID is the single-chunk variant of ChunkIDs, retained only so
|
||
// tasks enqueued by an interim per-chunk build still run (treated as a
|
||
// one-element batch). New enqueues use ChunkIDs.
|
||
ChunkID string `json:"chunk_id,omitempty"`
|
||
// BatchIndex is the 0-based ordinal of this batch inside the parent
|
||
// knowledge's text-chunk set, used as the subspan name suffix
|
||
// ("postprocess.question.batch[3]") so the timeline preserves order.
|
||
BatchIndex int `json:"batch_index,omitempty"`
|
||
// PrevChunkID / NextChunkID are the text chunks (by StartAt) just
|
||
// outside this batch window, computed at enqueue time so the worker can
|
||
// rebuild the same surrounding context the legacy whole-knowledge loop
|
||
// used at the batch boundaries, without re-listing every chunk of the
|
||
// knowledge. Empty when the batch is at a document boundary.
|
||
PrevChunkID string `json:"prev_chunk_id,omitempty"`
|
||
NextChunkID string `json:"next_chunk_id,omitempty"`
|
||
}
|
||
|
||
// SummaryGenerationPayload represents the summary generation task payload
|
||
type SummaryGenerationPayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
KnowledgeBaseID string `json:"knowledge_base_id"`
|
||
KnowledgeID string `json:"knowledge_id"`
|
||
Language string `json:"language,omitempty"`
|
||
// Refresh marks an independently queued refresh after document metadata or
|
||
// chunk edits. Refresh tasks are not part of the parse attempt's pending
|
||
// subtask counter and update existing summary chunks in place.
|
||
Refresh bool `json:"refresh,omitempty"`
|
||
// Attempt links this task to the parent parse attempt so the worker
|
||
// can record a postprocess.summary subspan under the right attempt's
|
||
// postprocess stage. See QuestionGenerationPayload.Attempt notes.
|
||
Attempt int `json:"attempt,omitempty"`
|
||
}
|
||
|
||
// KBClonePayload represents the knowledge base clone task payload
|
||
type KBClonePayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
TaskID string `json:"task_id"`
|
||
SourceID string `json:"source_id"`
|
||
TargetID string `json:"target_id"`
|
||
Initiator TaskInitiator `json:"initiator,omitempty"`
|
||
}
|
||
|
||
// IndexDeletePayload represents the index delete task payload
|
||
type IndexDeletePayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
KnowledgeBaseID string `json:"knowledge_base_id"`
|
||
EmbeddingModelID string `json:"embedding_model_id"`
|
||
KBType string `json:"kb_type"`
|
||
ChunkIDs []string `json:"chunk_ids"`
|
||
EffectiveEngines []RetrieverEngineParams `json:"effective_engines"`
|
||
// VectorStoreID is the bound store snapshot taken at enqueue time so the
|
||
// async worker can resolve the same store the KB was bound to.
|
||
// nil means the KB had no binding — falls back to EffectiveEngines.
|
||
VectorStoreID *string `json:"vector_store_id,omitempty"`
|
||
}
|
||
|
||
// KBDeletePayload represents the knowledge base delete task payload
|
||
type KBDeletePayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
KnowledgeBaseID string `json:"knowledge_base_id"`
|
||
DataSourceIDs []string `json:"data_source_ids,omitempty"`
|
||
EffectiveEngines []RetrieverEngineParams `json:"effective_engines"`
|
||
// VectorStoreID is the bound store snapshot taken at enqueue time (before
|
||
// soft-delete) so the async worker can resolve the right store. nil means
|
||
// the KB had no binding — falls back to EffectiveEngines.
|
||
VectorStoreID *string `json:"vector_store_id,omitempty"`
|
||
}
|
||
|
||
// KnowledgeListDeletePayload represents the batch knowledge delete task payload
|
||
type KnowledgeListDeletePayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
KnowledgeIDs []string `json:"knowledge_ids"`
|
||
Initiator TaskInitiator `json:"initiator,omitempty"`
|
||
}
|
||
|
||
// KnowledgeListReparsePayload represents the batch knowledge reparse task payload
|
||
type KnowledgeListReparsePayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
KnowledgeIDs []string `json:"knowledge_ids"`
|
||
ProcessConfig *KnowledgeProcessOverrides `json:"process_config,omitempty"`
|
||
Initiator TaskInitiator `json:"initiator,omitempty"`
|
||
}
|
||
|
||
// KnowledgeMovePayload represents the knowledge move task payload
|
||
type KnowledgeMovePayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
TaskID string `json:"task_id"`
|
||
KnowledgeIDs []string `json:"knowledge_ids"`
|
||
SourceKBID string `json:"source_kb_id"`
|
||
TargetKBID string `json:"target_kb_id"`
|
||
Mode string `json:"mode"` // "reuse_vectors" or "reparse"
|
||
Initiator TaskInitiator `json:"initiator,omitempty"`
|
||
}
|
||
|
||
// KnowledgeMoveProgress represents the progress of a knowledge move task
|
||
type KnowledgeMoveProgress struct {
|
||
TaskID string `json:"task_id"`
|
||
SourceKBID string `json:"source_kb_id"`
|
||
TargetKBID string `json:"target_kb_id"`
|
||
Status KBCloneTaskStatus `json:"status"`
|
||
Progress int `json:"progress"` // 0-100
|
||
Total int `json:"total"` // 总知识数
|
||
Processed int `json:"processed"` // 已处理数
|
||
Failed int `json:"failed"` // 失败数
|
||
Message string `json:"message"` // 状态消息
|
||
Error string `json:"error"` // 错误信息
|
||
CreatedAt int64 `json:"created_at"` // 任务创建时间
|
||
UpdatedAt int64 `json:"updated_at"` // 最后更新时间
|
||
}
|
||
|
||
// ManualProcessPayload represents the manual knowledge processing task payload.
|
||
// Used for both create (publish) and update operations.
|
||
type ManualProcessPayload struct {
|
||
TracingContext
|
||
RequestId string `json:"request_id"`
|
||
TenantID uint64 `json:"tenant_id"`
|
||
KnowledgeID string `json:"knowledge_id"`
|
||
KnowledgeBaseID string `json:"knowledge_base_id"`
|
||
Content string `json:"content"` // cleaned markdown content
|
||
NeedCleanup bool `json:"need_cleanup"` // true for update, false for create
|
||
}
|
||
|
||
// ImageMultimodalPayload represents the image multimodal processing task payload.
|
||
type ImageMultimodalPayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
KnowledgeID string `json:"knowledge_id"`
|
||
KnowledgeBaseID string `json:"knowledge_base_id"`
|
||
ChunkID string `json:"chunk_id"` // parent text chunk
|
||
ImageURL string `json:"image_url"` // provider:// URL (e.g. local://..., minio://...)
|
||
ImageLocalPath string `json:"image_local_path"` // deprecated: kept for backward compat with in-flight tasks
|
||
EnableOCR bool `json:"enable_ocr"`
|
||
EnableCaption bool `json:"enable_caption"`
|
||
Language string `json:"language,omitempty"` // Request locale for {{language}} in prompt templates
|
||
ImageSourceType string `json:"image_source_type,omitempty"` // Source type of the image (e.g., "scanned_pdf")
|
||
// Attempt links this image task back to the parent ProcessDocument
|
||
// attempt so the worker can record its image[i] subspan under the
|
||
// same attempt's multimodal stage span.
|
||
Attempt int `json:"attempt,omitempty"`
|
||
// ImageIndex is the 0-based ordinal of this image inside the
|
||
// parent's image set. Used as the subspan name suffix
|
||
// ("multimodal.image[3]") so the timeline preserves order.
|
||
ImageIndex int `json:"image_index,omitempty"`
|
||
}
|
||
|
||
// KnowledgePostProcessPayload represents the knowledge post process task payload.
|
||
type KnowledgePostProcessPayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
KnowledgeID string `json:"knowledge_id"`
|
||
KnowledgeBaseID string `json:"knowledge_base_id"`
|
||
Language string `json:"language,omitempty"` // Request locale for {{language}} in prompt templates
|
||
Attempt int `json:"attempt,omitempty"`
|
||
}
|
||
|
||
// KnowledgeAutoTagPayload identifies a document whose parsed content should be
|
||
// classified against the latest set of existing tags in its knowledge base.
|
||
type KnowledgeAutoTagPayload struct {
|
||
TracingContext
|
||
TenantID uint64 `json:"tenant_id"`
|
||
KnowledgeID string `json:"knowledge_id"`
|
||
KnowledgeBaseID string `json:"knowledge_base_id"`
|
||
Language string `json:"language,omitempty"`
|
||
Attempt int `json:"attempt,omitempty"`
|
||
}
|
||
|
||
// KBCloneTaskStatus represents the status of a knowledge base clone task
|
||
type KBCloneTaskStatus string
|
||
|
||
const (
|
||
KBCloneStatusPending KBCloneTaskStatus = "pending"
|
||
KBCloneStatusProcessing KBCloneTaskStatus = "processing"
|
||
KBCloneStatusCompleted KBCloneTaskStatus = "completed"
|
||
KBCloneStatusFailed KBCloneTaskStatus = "failed"
|
||
)
|
||
|
||
// KBCloneProgress represents the progress of a knowledge base clone task
|
||
type KBCloneProgress struct {
|
||
TaskID string `json:"task_id"`
|
||
SourceID string `json:"source_id"`
|
||
TargetID string `json:"target_id"`
|
||
Status KBCloneTaskStatus `json:"status"`
|
||
Progress int `json:"progress"` // 0-100
|
||
Total int `json:"total"` // 总知识数
|
||
Processed int `json:"processed"` // 已处理数
|
||
Message string `json:"message"` // 状态消息
|
||
Error string `json:"error"` // 错误信息
|
||
CreatedAt int64 `json:"created_at"` // 任务创建时间
|
||
UpdatedAt int64 `json:"updated_at"` // 最后更新时间
|
||
}
|