Files
Agent-Squad/textextract/textextract.go
weichenandCursor bfec3c8930
ci / vet-and-test (push) Failing after 3s
ci / docker-checkout-hostgateway (push) Failing after 3s
Import agent-squad as a standalone repository.
Pin Agent-Native as a submodule so the module builds without the multi-repo sibling path.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-29 12:38:35 +00:00

126 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package textextract 从输入材料(txt/md/json/yaml/docx)提取纯文本,
// 并注入页码锚点便于引用(机制语义对齐 Agent-System internal/textextract,
// 为独立底座做的纯标准库精简版)。
package textextract
import (
"archive/zip"
"bytes"
"encoding/xml"
"fmt"
"io"
"path/filepath"
"regexp"
"strings"
)
// ExtractTextWithAnchors 提取文本并注入页码锚点。
func ExtractTextWithAnchors(filename string, data []byte) (string, error) {
ext := strings.ToLower(filepath.Ext(filename))
switch ext {
case ".txt", ".md", ".json", ".yaml", ".yml", ".csv", ".log":
return InjectPageAnchors(string(data)), nil
case ".docx":
text, err := docxText(data)
if err != nil {
return "", fmt.Errorf("docx %s: %w", filename, err)
}
return InjectPageAnchors(text), nil
default:
// 二进制降级:提取可见 ASCII/UTF-8 片段。
return InjectPageAnchors(extractVisibleStrings(data)), nil
}
}
// InjectPageAnchors 每 pageSizeAt 字符(或遇到 \f 换页符)注入 <!-- page N -->。
func InjectPageAnchors(s string) string {
if s == "" {
return s
}
var b strings.Builder
page := 1
count := 0
fprintf := func() { fmt.Fprintf(&b, "\n\n<!-- page %d -->\n", page) }
fprintf()
for _, r := range s {
b.WriteRune(r)
count++
if r == '\f' {
page++
count = 0
fprintf()
continue
}
if count >= 3000 {
page++
count = 0
fprintf()
}
}
return b.String()
}
var xmlTag = regexp.MustCompile(`</w:p>`)
func docxText(data []byte) (string, error) {
zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
return "", err
}
for _, f := range zr.File {
if f.Name != "word/document.xml" {
continue
}
rc, err := f.Open()
if err != nil {
return "", err
}
defer rc.Close()
raw, err := io.ReadAll(rc)
if err != nil {
return "", err
}
// 段落结尾转换行,再剥离全部 XML 标签。
raw = xmlTag.ReplaceAll(raw, []byte("\n"))
dec := xml.NewDecoder(bytes.NewReader(raw))
var b strings.Builder
for {
tok, err := dec.Token()
if err == io.EOF {
break
}
if err != nil {
return "", err
}
if t, ok := tok.(xml.CharData); ok {
b.Write(t)
}
}
return strings.TrimSpace(b.String()), nil
}
return "", fmt.Errorf("word/document.xml not found")
}
// extractVisibleStrings 从二进制中提取可见可打印字符串(长度>=4)。
func extractVisibleStrings(data []byte) string {
var out []string
start := -1
for i, b := range data {
printable := (b >= 0x20 && b < 0x7f) || b >= 0x80
if printable {
if start < 0 {
start = i
}
continue
}
if start >= 0 && i-start >= 4 {
out = append(out, string(data[start:i]))
}
start = -1
}
if start >= 0 && len(data)-start >= 4 {
out = append(out, string(data[start:]))
}
return strings.Join(out, "\n")
}