mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-09 12:54:47 +08:00
Ports remaining Go parser wiring and PDF backends, adds tenant-aware VLM dispatch, aligns post-processing with Python, and adds end-to-end pipeline coverage with a generated six-page PDF.
110 lines
2.6 KiB
Go
110 lines
2.6 KiB
Go
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
func pdfFileMeta(filename string, pageCount int) map[string]any {
|
|
if pageCount < 0 {
|
|
pageCount = 0
|
|
}
|
|
return map[string]any{
|
|
"name": filename,
|
|
"page_count": pageCount,
|
|
"outline": []map[string]any{},
|
|
}
|
|
}
|
|
|
|
func pdfItemsToResult(filename string, items []map[string]any, outputFormat string, pageCount int) ParseResult {
|
|
if len(items) == 0 {
|
|
items = []map[string]any{{"text": "", "doc_type_kwd": "text"}}
|
|
}
|
|
pageCount = normalizePDFPageCount(pageCount, items)
|
|
switch strings.ToLower(strings.TrimSpace(outputFormat)) {
|
|
case "", "json":
|
|
return ParseResult{
|
|
OutputFormat: "json",
|
|
File: pdfFileMeta(filename, pageCount),
|
|
JSON: items,
|
|
}
|
|
case "markdown":
|
|
var b strings.Builder
|
|
for _, item := range items {
|
|
text, _ := item["text"].(string)
|
|
layout, _ := item["layout"].(string)
|
|
if strings.TrimSpace(text) == "" {
|
|
continue
|
|
}
|
|
if layout == "title" && !strings.HasPrefix(strings.TrimSpace(text), "#") {
|
|
b.WriteString("## ")
|
|
}
|
|
b.WriteString(text)
|
|
b.WriteString("\n\n")
|
|
}
|
|
return ParseResult{
|
|
OutputFormat: "markdown",
|
|
File: pdfFileMeta(filename, pageCount),
|
|
Markdown: strings.TrimRight(b.String(), "\n"),
|
|
}
|
|
default:
|
|
return ParseResult{Err: fmt.Errorf("parser: unsupported PDF output_format %q", outputFormat)}
|
|
}
|
|
}
|
|
|
|
func normalizePDFPageCount(fallback int, items []map[string]any) int {
|
|
if inferred := inferPDFPageCountFromItems(items); inferred > fallback {
|
|
return inferred
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func inferPDFPageCountFromItems(items []map[string]any) int {
|
|
pages := map[int]struct{}{}
|
|
for _, item := range items {
|
|
for page := range collectPDFPageNumbers(item) {
|
|
pages[page] = struct{}{}
|
|
}
|
|
}
|
|
return len(pages)
|
|
}
|
|
|
|
func collectPDFPageNumbers(raw any) map[int]struct{} {
|
|
pages := map[int]struct{}{}
|
|
var walk func(any)
|
|
walk = func(node any) {
|
|
switch v := node.(type) {
|
|
case map[string]any:
|
|
for _, key := range []string{"page_number", "page_num", "page_no", "page_index", "page_idx", "page"} {
|
|
if page := int(numberValue(v[key])); page > 0 {
|
|
pages[page] = struct{}{}
|
|
}
|
|
}
|
|
for _, key := range []string{"_pdf_positions", "positions"} {
|
|
if positions, ok := v[key].([][]any); ok {
|
|
for _, pos := range positions {
|
|
if len(pos) > 0 {
|
|
if page := int(numberValue(pos[0])); page > 0 {
|
|
pages[page] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for _, child := range v {
|
|
walk(child)
|
|
}
|
|
case []any:
|
|
for _, child := range v {
|
|
walk(child)
|
|
}
|
|
case []map[string]any:
|
|
for _, child := range v {
|
|
walk(child)
|
|
}
|
|
}
|
|
}
|
|
walk(raw)
|
|
return pages
|
|
}
|