mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-11 22:25:41 +08:00
Two refactors on the Go port (agent-go-port): - Remove the dead per-tenant canvas runtime selector (write-only Redis scaffolding with no runtime callers) and its dependent metrics/admin code. - Move the tokenizer embedding-model id from the shared ingestion globals into a Tokenizer-scoped setup, and wire the production embedder resolver in the ingestion task package. 32 files changed, 861 insertions, 1228 deletions.
247 lines
7.9 KiB
Go
247 lines
7.9 KiB
Go
package task
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"ragflow/internal/entity"
|
|
)
|
|
|
|
func testStrPtr(s string) *string { return &s }
|
|
|
|
func makeTaskHandlerTestContext(pipelineID string) *TaskContext {
|
|
return &TaskContext{
|
|
IngestionTask: &entity.IngestionTask{
|
|
ID: "task-1",
|
|
DocumentID: "doc-1",
|
|
},
|
|
PipelineID: pipelineID,
|
|
Doc: entity.Document{
|
|
ID: "doc-1",
|
|
KbID: "kb-1",
|
|
Name: testStrPtr("test-doc.pdf"),
|
|
ParserID: "naive",
|
|
ParserConfig: entity.JSONMap{},
|
|
},
|
|
KB: entity.Knowledgebase{
|
|
ID: "kb-1",
|
|
TenantID: "tenant-1",
|
|
EmbdID: "embd-1",
|
|
},
|
|
Tenant: entity.Tenant{
|
|
ID: "tenant-1",
|
|
LLMID: "gpt-4",
|
|
},
|
|
ProgressFunc: func(prog float64, msg string) {},
|
|
}
|
|
}
|
|
|
|
func newNoopDataflowService(ctx *TaskContext, dataflowID string) (*PipelineExecutor, error) {
|
|
if strings.TrimSpace(dataflowID) == "" {
|
|
dataflowID = "flow-1"
|
|
}
|
|
svc, err := NewDataflowService(ctx, dataflowID, 0, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
svc = svc.
|
|
WithLoadDSLFunc(func(ctx context.Context, dataflowID string) (string, string, error) {
|
|
return `{"nodes":[{"id":"stub-node"}],"edges":[]}`, dataflowID, nil
|
|
}).
|
|
WithRunPipelineFunc(func(ctx context.Context, dsl string) (map[string]any, string, error) {
|
|
return map[string]any{
|
|
"chunks": []map[string]any{{
|
|
"text": "stub dataflow chunk",
|
|
"q_2_vec": []float64{0.1, 0.2},
|
|
}},
|
|
}, dsl, nil
|
|
}).
|
|
WithInsertChunksFunc(func(ctx context.Context, chunks []map[string]any, baseName, datasetID string) ([]string, error) {
|
|
return nil, nil
|
|
}).
|
|
WithLogCreateFunc(func(log *entity.PipelineOperationLog) error {
|
|
return nil
|
|
}).
|
|
WithDocService(&stubDocService{}).
|
|
WithChunkCounter(&stubChunkCounter{})
|
|
return svc, nil
|
|
}
|
|
|
|
func newNoopTaskHandler(ctx *TaskContext) *TaskHandler {
|
|
return NewTaskHandler(ctx).WithDataflowServiceFactory(newNoopDataflowService)
|
|
}
|
|
|
|
func TestTaskHandler_HandleRejectsNilContext(t *testing.T) {
|
|
if err := NewTaskHandler(nil).Handle(); err == nil {
|
|
t.Fatal("expected error for nil context")
|
|
}
|
|
}
|
|
|
|
func TestTaskHandler_HandleRequiresPipelineID(t *testing.T) {
|
|
ctx := makeTaskHandlerTestContext("")
|
|
handler := NewTaskHandler(ctx)
|
|
if err := handler.Handle(); err == nil {
|
|
t.Fatal("expected error for empty pipeline id")
|
|
}
|
|
}
|
|
|
|
func TestTaskHandler_DefaultDataflowServiceInjectsProgress(t *testing.T) {
|
|
ctx := makeTaskHandlerTestContext("flow-1")
|
|
handler := NewTaskHandler(ctx).WithDataflowServiceFactory(func(ctx *TaskContext, dataflowID string) (*PipelineExecutor, error) {
|
|
svc, err := NewDataflowService(ctx, dataflowID, 0, 0)
|
|
if err != nil {
|
|
t.Fatalf("NewDataflowService: %v", err)
|
|
}
|
|
if svc.progressFunc == nil {
|
|
t.Fatal("expected default progress func to be injected")
|
|
}
|
|
return newNoopDataflowService(ctx, dataflowID)
|
|
})
|
|
if err := handler.Handle(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestTaskHandler_Dataflow_UsesTaskContext(t *testing.T) {
|
|
ctx := makeTaskHandlerTestContext("flow-1")
|
|
type ctxKey string
|
|
const key ctxKey = "trace"
|
|
ctx.Ctx = context.WithValue(context.Background(), key, "task-ctx")
|
|
|
|
handler := NewTaskHandler(ctx).WithDataflowServiceFactory(func(ctx *TaskContext, dataflowID string) (*PipelineExecutor, error) {
|
|
return mustNewDataflowService(t, ctx, dataflowID, 0, 0).
|
|
WithLoadDSLFunc(func(ctx context.Context, dataflowID string) (string, string, error) {
|
|
return `{"nodes":[{"id":"stub-node"}],"edges":[]}`, dataflowID, nil
|
|
}).
|
|
WithRunPipelineFunc(func(runCtx context.Context, dsl string) (map[string]any, string, error) {
|
|
if got := runCtx.Value(key); got != "task-ctx" {
|
|
t.Fatalf("runCtx value = %v, want task-ctx", got)
|
|
}
|
|
return map[string]any{"chunks": []map[string]any{{"text": "stub", "q_2_vec": []float64{0.1, 0.2}}}}, dsl, nil
|
|
}).
|
|
WithInsertChunksFunc(func(ctx context.Context, chunks []map[string]any, baseName, datasetID string) ([]string, error) {
|
|
return nil, nil
|
|
}).
|
|
WithLogCreateFunc(func(log *entity.PipelineOperationLog) error { return nil }).
|
|
WithDocService(&stubDocService{}).
|
|
WithChunkCounter(&stubChunkCounter{}), nil
|
|
})
|
|
if err := handler.Handle(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestTaskHandler_Dataflow_UsesBackgroundContextWhenMissing(t *testing.T) {
|
|
ctx := makeTaskHandlerTestContext("flow-1")
|
|
|
|
handler := NewTaskHandler(ctx).WithDataflowServiceFactory(func(ctx *TaskContext, dataflowID string) (*PipelineExecutor, error) {
|
|
return mustNewDataflowService(t, ctx, dataflowID, 0, 0).
|
|
WithLoadDSLFunc(func(ctx context.Context, dataflowID string) (string, string, error) {
|
|
return `{"nodes":[{"id":"stub-node"}],"edges":[]}`, dataflowID, nil
|
|
}).
|
|
WithRunPipelineFunc(func(runCtx context.Context, dsl string) (map[string]any, string, error) {
|
|
if runCtx == nil {
|
|
t.Fatal("runCtx is nil")
|
|
}
|
|
return map[string]any{"chunks": []map[string]any{{"text": "stub", "q_2_vec": []float64{0.1, 0.2}}}}, dsl, nil
|
|
}).
|
|
WithInsertChunksFunc(func(ctx context.Context, chunks []map[string]any, baseName, datasetID string) ([]string, error) {
|
|
return nil, nil
|
|
}).
|
|
WithLogCreateFunc(func(log *entity.PipelineOperationLog) error { return nil }).
|
|
WithDocService(&stubDocService{}).
|
|
WithChunkCounter(&stubChunkCounter{}), nil
|
|
})
|
|
if err := handler.Handle(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestTaskHandler_Dataflow_ShowsProgressAndPipelineLog(t *testing.T) {
|
|
ctx := makeTaskHandlerTestContext("flow-1")
|
|
ctx.Doc.PipelineID = testStrPtr("flow-1")
|
|
ctx.Doc.Name = testStrPtr("verify-dataflow.pdf")
|
|
|
|
var pipelineCalled bool
|
|
var insertCalled bool
|
|
var logCreateCalls int
|
|
var insertedChunkCount int
|
|
var progressProgs []float64
|
|
var progressMsgs []string
|
|
|
|
ctx.ProgressFunc = func(prog float64, msg string) {
|
|
progressProgs = append(progressProgs, prog)
|
|
progressMsgs = append(progressMsgs, msg)
|
|
}
|
|
|
|
handler := NewTaskHandler(ctx).WithDataflowServiceFactory(func(ctx *TaskContext, dataflowID string) (*PipelineExecutor, error) {
|
|
svc := mustNewDataflowService(t, ctx, dataflowID, 0, 0).
|
|
WithLoadDSLFunc(func(ctx context.Context, dataflowID string) (string, string, error) {
|
|
return `{"nodes":[{"id":"stub-node"}],"edges":[]}`, dataflowID, nil
|
|
}).
|
|
WithRunPipelineFunc(func(ctx context.Context, dsl string) (map[string]any, string, error) {
|
|
pipelineCalled = true
|
|
return map[string]any{
|
|
"chunks": []map[string]any{{
|
|
"text": "stub dataflow chunk",
|
|
"q_2_vec": []float64{0.1, 0.2},
|
|
}},
|
|
}, dsl, nil
|
|
}).
|
|
WithInsertChunksFunc(func(ctx context.Context, chunks []map[string]any, baseName, datasetID string) ([]string, error) {
|
|
insertCalled = true
|
|
insertedChunkCount = len(chunks)
|
|
return nil, nil
|
|
}).
|
|
WithLogCreateFunc(func(log *entity.PipelineOperationLog) error {
|
|
logCreateCalls++
|
|
return nil
|
|
}).
|
|
WithDocService(&stubDocService{}).
|
|
WithChunkCounter(&stubChunkCounter{})
|
|
return svc, nil
|
|
})
|
|
|
|
if err := handler.Handle(); err != nil {
|
|
t.Fatalf("handler.Handle() error: %v", err)
|
|
}
|
|
|
|
if !pipelineCalled {
|
|
t.Fatal("expected mock pipeline.run to be called")
|
|
}
|
|
if !insertCalled {
|
|
t.Fatal("expected insertChunks to be called")
|
|
}
|
|
if insertedChunkCount != 1 {
|
|
t.Fatalf("insertedChunkCount = %d, want 1", insertedChunkCount)
|
|
}
|
|
if len(progressProgs) == 0 {
|
|
t.Fatal("expected progress callbacks, got none")
|
|
}
|
|
|
|
foundStartIndex := false
|
|
for _, msg := range progressMsgs {
|
|
if strings.Contains(msg, "Start to index") {
|
|
foundStartIndex = true
|
|
break
|
|
}
|
|
}
|
|
if !foundStartIndex {
|
|
t.Fatalf("expected progress message containing %q, got %v", "Start to index", progressMsgs)
|
|
}
|
|
|
|
if got := progressProgs[len(progressProgs)-1]; got != 1.0 {
|
|
t.Fatalf("final progress = %v, want 1.0", got)
|
|
}
|
|
|
|
lastMsg := progressMsgs[len(progressMsgs)-1]
|
|
if !strings.Contains(lastMsg, "Indexing done") {
|
|
t.Fatalf("final progress msg = %q, want substring %q", lastMsg, "Indexing done")
|
|
}
|
|
|
|
if logCreateCalls != 1 {
|
|
t.Fatalf("logCreateCalls = %d, want 1", logCreateCalls)
|
|
}
|
|
}
|