mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-25 01:43:27 +08:00
## Summary Adds page-range parsing support to the Go-native pipeline path and introduces strict `parse_type` validation for both dataset and document update endpoints. ## What changed ### Pages range parsing - **`internal/utility/pdf_pages.go`** — `NormalizePDFPages`: normalizes raw page ranges (list of `[from,to]` 1-indexed inclusive ranges) into sorted, merged, deduplicated `[][]int`. Invalid ranges are dropped. - **`internal/ingestion/pipeline/pdf_pages.go`** — `NormalizeParserConfigPages`: walks any parser_config map and normalizes `"pages"` values under every component → filetype setup, so the persisted config always carries clean, merged ranges. - **`internal/deepdoc/parser/pdf/parser.go`** — integrates `resolvePagesToProcess` to filter parsed PDF pages by the configured ranges. - Pipeline integration (parser pages): `internal/parser/parser/pdf_parser_common.go`, `chunk_process.go`, plus associated e2e and unit tests. ### Parse type validation (shared logic) - **`internal/service/parser_mode.go`** (new) — `ValidateParseTypeMode`: shared function that validates `parse_type` (1=BuiltIn/parser_id, 2=Pipeline/pipeline_id) and ensures the corresponding field is present. Used by both dataset and document update endpoints. - **`internal/service/dataset/crud.go`** / `update.go` — replaces inline `isPipelineMode`/`isBuiltinMode` computation with the shared `service.ValidateParseTypeMode`. - **`internal/service/document/document_dataset_update.go`** — adds strict `parse_type` validation in `validateDatasetDocumentUpdate`, simplifies the reparse logic to a two-way switch (isBuiltin/isPipeline) now that parse_type is always valid. - **`internal/service/document/document.go`** — adds `ParseType` field to `UpdateDatasetDocumentRequest`. - **`internal/service/document/document_dataset_update.go`** — `updateDocumentParserConfig` fallback path when DSL loading fails. - **`internal/service/parser_mode_test.go`** (new) — test coverage for nil, invalid, and missing-field scenarios. ### Frontend - **`web/src/interfaces/request/document.ts`** — adds `parseType` to `IChangeParserRequestBody`. - **`web/src/hooks/use-document-request.ts`** — `useSetDocumentPipelineParser` sends `parse_type` in the PATCH payload. - **`web/src/pages/dataset/dataset/use-change-document-parser.ts`** — Go/Python branching for the document parser config dialog. - **`web/src/components/document-pipeline-dialog/use-document-pipeline-form.ts`** — `buildSubmitData` returns `parseType` (bugfix: was dropped from the return value). ### Test changes - **Removed**: 2 tests that verified the old "mutually exclusive" error (replaced by `ValidateParseTypeMode` coverage). - **Modified**: 6 tests across document and dataset packages to include `ParseType` in request structs. - **Added**: new e2e tests for pages parsing (`pages_e2e_test.go`, `pdf_parser_pages_e2e_test.go`) and unit tests for `NormalizePDFPages`, `NormalizeParserConfigPages`, `resolvePagesToProcess`. ## Backward compatibility - The `parse_type` field is **required** when `parser_id` or `pipeline_id` is sent. This changes the contract for both dataset and document PATCH endpoints, but aligns the Go backend with the existing frontend behavior (the frontend already sends `parse_type`). Callers that omit `parse_type` when updating parser/pipeline selections will receive a clear error message. - Existing callers that only update fields like `name`, `enabled`, or `meta_fields` are unaffected. - Test updates ensure all known call sites are compliant.
89 lines
2.3 KiB
Go
89 lines
2.3 KiB
Go
//go:build cgo && manual
|
|
|
|
package pdf
|
|
|
|
import (
|
|
"context"
|
|
"image/png"
|
|
"os"
|
|
"ragflow/internal/common"
|
|
inf "ragflow/internal/deepdoc/parser/pdf/inference"
|
|
pdftype "ragflow/internal/deepdoc/parser/pdf/type"
|
|
util "ragflow/internal/deepdoc/parser/pdf/util"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestOCR_mergeChars_RealScanned tests ocrMergeChars on a real scanned
|
|
// medical PDF where pdf_oxide extracts noise (RASB@PS, random symbols)
|
|
// instead of real text. This validates that detect+merge+recognize
|
|
// produces readable English from the scan.
|
|
func TestOCR_mergeChars_RealScanned(t *testing.T) {
|
|
url := common.GetEnv(common.EnvDeepDocURL)
|
|
if url == "" {
|
|
t.Skip("DEEPDOC_URL not set")
|
|
}
|
|
dd, err := inf.NewClient(url)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !dd.Health() {
|
|
t.Fatal("DeepDoc not available")
|
|
}
|
|
|
|
pdfPath := "testdata/real_pdfs/1例3个月喉噗合并先天性心脏病患儿气管插管的麻醉护理.pdf"
|
|
data, err := os.ReadFile(pdfPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
eng, err := NewEngine(data)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
chars, err := eng.ExtractChars(0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Logf("pdf_oxide Chars: %d", len(chars))
|
|
|
|
var sample strings.Builder
|
|
for i, c := range chars {
|
|
if i >= 200 {
|
|
break
|
|
}
|
|
sample.WriteString(c.Text)
|
|
}
|
|
t.Logf("pdf_oxide sample: %q", sample.String())
|
|
t.Logf("isScanNoise: %v", util.IsScanNoise(sample.String()))
|
|
t.Logf("isGarbledPage: %v", util.IsGarbledPage(chars))
|
|
|
|
img, err := eng.RenderPageImage(0, 72*3)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
p := NewParser(pdftype.ParserConfig{})
|
|
|
|
boxes := p.ocrMergeChars(context.Background(), img, chars, dd, 0)
|
|
t.Logf("ocrMergeChars boxes: %d", len(boxes))
|
|
for i, b := range boxes {
|
|
// Save go render for comparison
|
|
f, _ := os.Create("/tmp/_go_render.png")
|
|
png.Encode(f, img)
|
|
f.Close()
|
|
t.Logf("Go render saved: %v -> /tmp/_go_render.png", img.Bounds())
|
|
end := min(120, len(b.Text))
|
|
t.Logf(" [%d] (%.0f,%.0f)-(%.0f,%.0f) text=%q",
|
|
i, b.X0, b.Top, b.X1, b.Bottom, b.Text[:end])
|
|
}
|
|
|
|
scanBoxes := p.ocrDetectAndRecognize(context.Background(), img, dd, 0, "scan page")
|
|
t.Logf("ocrScanPage boxes (no chars): %d", len(scanBoxes))
|
|
for i, b := range scanBoxes {
|
|
end := min(120, len(b.Text))
|
|
t.Logf(" [%d] (%.0f,%.0f)-(%.0f,%.0f) text=%q",
|
|
i, b.X0, b.Top, b.X1, b.Bottom, b.Text[:end])
|
|
}
|
|
}
|