Skip to content

Commit aa58642

Browse files
committed
feat: generalize text extraction to office docs
- swapped mimetype detect with gabriel-vasile/mimetype
1 parent 47a2119 commit aa58642

5 files changed

Lines changed: 36 additions & 19 deletions

File tree

‎backend/cmd/chat/utils.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func buildContext(convID string, start int, user string) []providers.SimpleMessa
127127
// embed docs and encode the rest if agentic retrieval is on, otherwise just provide links to files
128128
for _, att := range msg.Attachments {
129129

130-
if att.File.Type == "application/pdf" && agenticRetrieval {
130+
if fs.IsRetrievableDoc(att.File.Type) && agenticRetrieval {
131131
// content of first page, model will figure to use tools to get rest of content if needed
132132
msg.Content += embeddedAttachment(att)
133133
continue

‎backend/cmd/files/pdf-utils.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ var (
2323
matrixRe = regexp.MustCompile(`transform:matrix\([^,]+,[^,]+,[^,]+,[^,]+,\s*([-0-9.]+)\s*,\s*([-0-9.]+)\s*\)`)
2424
)
2525

26-
func readPDFPages(path string, fileID string) ([]FilePage, error) {
26+
func readDocPages(path string, fileID string) ([]FilePage, error) {
2727
doc, err := fitz.New(path)
2828
if err != nil {
2929
return nil, err
@@ -53,7 +53,7 @@ func readPDFPages(path string, fileID string) ([]FilePage, error) {
5353
Content: pageText,
5454
})
5555
}
56-
log.Debug("Extracted pages from PDF", "pages", len(pages))
56+
log.Debug("Extracted pages from document", "pages", len(pages))
5757
return pages, nil
5858
}
5959

‎backend/cmd/files/utils.go‎

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import (
99
"image/png"
1010
"io"
1111
"mime/multipart"
12-
"net/http"
1312
"os"
1413
"path"
1514
"strconv"
1615
"strings"
1716
"time"
1817

1918
"github.com/Bajahaw/ai-ui/cmd/providers"
19+
"github.com/gabriel-vasile/mimetype"
2020

2121
"github.com/google/uuid"
2222
)
@@ -135,27 +135,24 @@ func saveUploadedFile(file multipart.File, handler *multipart.FileHeader, user s
135135
}
136136

137137
func detectFileType(file multipart.File) (string, error) {
138-
buf := make([]byte, 512)
139-
n, err := file.Read(buf)
140-
if err != nil && err != io.EOF {
138+
mtype, err := mimetype.DetectReader(file)
139+
if err != nil {
141140
return "", err
142141
}
143-
if n == 0 {
142+
if mtype.String() == "" {
144143
return "", fmt.Errorf("empty file")
145144
}
146145

147-
fileType := http.DetectContentType(buf[:n])
148-
log.Debug("Uploaded file type", "type", fileType)
146+
log.Debug("Uploaded file type", "type", mtype.String())
149147

150-
// Reset file read pointer: prefer Seek if available
148+
// Reset file read pointer
151149
if seeker, ok := file.(io.Seeker); ok {
152-
_, err = seeker.Seek(0, io.SeekStart)
153-
if err != nil {
150+
if _, err := seeker.Seek(0, io.SeekStart); err != nil {
154151
return "", err
155152
}
156153
}
157154

158-
return fileType, nil
155+
return mtype.String(), nil
159156
}
160157

161158
// compressImage reads an image from r and re-encodes it with conservative
@@ -210,21 +207,21 @@ func extractFileContent(file File, model string) (string, error) {
210207
return string(fileContent), nil
211208
}
212209

213-
if file.Type == "application/pdf" {
214-
pages, err := readPDFPages(file.Path, file.ID)
210+
if IsRetrievableDoc(file.Type) {
211+
pages, err := readDocPages(file.Path, file.ID)
215212
if err != nil {
216-
log.Error("Error reading PDF pages", "err", err)
213+
log.Error("Error reading document pages", "err", err, "type", file.Type)
217214
return "", err
218215
}
219216

220217
// indexing all pages individually to allow retrieval of specific pages later.
221218
err = repo.SavePages(pages)
222219
if err != nil {
223-
log.Error("Error saving PDF pages", "err", err)
220+
log.Error("Error saving document pages", "err", err)
224221
return "", err
225222
}
226223

227-
return "PDF content page 1: \n\n" + pages[0].Content + "... retrieve rest of content using tools", nil
224+
return "Document content page 1: \n\n" + pages[0].Content + "... retrieve rest of content using tools", nil
228225
}
229226

230227
if strings.HasPrefix(file.Type, "image/") {
@@ -259,3 +256,20 @@ func extractFileContent(file File, model string) (string, error) {
259256

260257
return "", fmt.Errorf("unsupported file type for content extraction: %s", file.Type)
261258
}
259+
260+
// IsRetrievableDoc returns true for document types supported by go-fitz
261+
// that can be page-extracted and searched via agentic retrieval tools.
262+
func IsRetrievableDoc(mimeType string) bool {
263+
switch mimeType {
264+
case "application/pdf",
265+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
266+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
267+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
268+
"application/vnd.oasis.opendocument.text",
269+
"application/vnd.oasis.opendocument.presentation",
270+
"application/vnd.oasis.opendocument.spreadsheet",
271+
"application/epub+zip":
272+
return true
273+
}
274+
return false
275+
}

‎backend/go.mod‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.25.5
55
require (
66
github.com/charmbracelet/log v1.0.0
77
github.com/evgensoft/ddgo v0.0.0-20250604183002-54c292420e4d
8+
github.com/gabriel-vasile/mimetype v1.4.13
89
github.com/gen2brain/go-fitz v1.24.15
910
github.com/golang-jwt/jwt/v5 v5.3.1
1011
github.com/google/uuid v1.6.0

‎backend/go.sum‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0o
3030
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
3131
github.com/evgensoft/ddgo v0.0.0-20250604183002-54c292420e4d h1:V9TIlV+eSq9hrPOZs7Kcy5vUQxvros/8EekC3lewb/4=
3232
github.com/evgensoft/ddgo v0.0.0-20250604183002-54c292420e4d/go.mod h1:S42fkdocqGLIlRR2F3cJMp5yTv6C8SRI8ppzIPhuhTc=
33+
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
34+
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
3335
github.com/gen2brain/go-fitz v1.24.15 h1:sJNB1MOWkqnzzENPHggFpgxTwW0+S5WF/rM5wUBpJWo=
3436
github.com/gen2brain/go-fitz v1.24.15/go.mod h1:SftkiVbTHqF141DuiLwBBM65zP7ig6AVDQpf2WlHamo=
3537
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=

0 commit comments

Comments
 (0)