From 3082e558adca612fbbd60a430294fca03e1e2143 Mon Sep 17 00:00:00 2001
From: SEWADE <31615609+SEWADE@users.noreply.github.com>
Date: Wed, 1 Apr 2026 09:03:09 +0200
Subject: [PATCH 1/3] fix: add ExtraData to CustomField struct for select
options
---
paperless.go | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/paperless.go b/paperless.go
index 170658db..e4d716ee 100644
--- a/paperless.go
+++ b/paperless.go
@@ -39,10 +39,20 @@ type PaperlessClient struct {
}
// CustomField represents a custom field from the Paperless-ngx API
+type SelectOption struct {
+ ID string `json:"id"`
+ Label string `json:"label"`
+}
+
+type CustomFieldExtraData struct {
+ SelectOptions []SelectOption `json:"select_options"`
+}
+
type CustomField struct {
- ID int `json:"id"`
- Name string `json:"name"`
- DataType string `json:"data_type"`
+ ID int `json:"id"`
+ Name string `json:"name"`
+ DataType string `json:"data_type"`
+ ExtraData *CustomFieldExtraData `json:"extra_data"`
}
// DocumentType represents a document type from the Paperless-ngx API
From f24c3d1dffe6a04d8b0380a4ca343e61c613a95c Mon Sep 17 00:00:00 2001
From: SEWADE <31615609+SEWADE@users.noreply.github.com>
Date: Wed, 1 Apr 2026 09:19:43 +0200
Subject: [PATCH 2/3] fix: include select options in LLM prompt for custom
fields
---
app_llm.go | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/app_llm.go b/app_llm.go
index 98bd5d93..21c11558 100644
--- a/app_llm.go
+++ b/app_llm.go
@@ -401,7 +401,15 @@ func (app *App) getSuggestedCustomFields(ctx context.Context, doc Document, sele
var xmlBuilder strings.Builder
xmlBuilder.WriteString("\n")
for _, field := range selectedCustomFields {
- xmlBuilder.WriteString(fmt.Sprintf(" \n", field.Name, field.DataType))
+ if field.DataType == "select" && field.ExtraData != nil && len(field.ExtraData.SelectOptions) > 0 {
+ xmlBuilder.WriteString(fmt.Sprintf(" \n", field.Name, field.DataType))
+ for _, opt := range field.ExtraData.SelectOptions {
+ xmlBuilder.WriteString(fmt.Sprintf(" \n", opt.ID, opt.Label))
+ }
+ xmlBuilder.WriteString(" \n")
+ } else {
+ xmlBuilder.WriteString(fmt.Sprintf(" \n", field.Name, field.DataType))
+ }
}
xmlBuilder.WriteString("")
customFieldsXML := xmlBuilder.String()
From c8b434f48cc61b4a9f9fc70887f2d500dc86c3ad Mon Sep 17 00:00:00 2001
From: SEWADE <31615609+SEWADE@users.noreply.github.com>
Date: Wed, 1 Apr 2026 10:23:51 +0200
Subject: [PATCH 3/3] fix: escape XML attributes and text in custom field
prompt
---
app_llm.go | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/app_llm.go b/app_llm.go
index 21c11558..422526ad 100644
--- a/app_llm.go
+++ b/app_llm.go
@@ -373,7 +373,22 @@ func (app *App) getSuggestedCreatedDate(ctx context.Context, content string, log
result := stripReasoning(completion.Choices[0].Content)
return strings.TrimSpace(strings.Trim(result, "\"")), nil
}
+var xmlAttrEscaper = strings.NewReplacer(
+ "&", "&",
+ `"`, """,
+ "'", "'",
+ "<", "<",
+ ">", ">",
+)
+
+var xmlTextEscaper = strings.NewReplacer(
+ "&", "&",
+ "<", "<",
+ ">", ">",
+)
+func escapeXMLAttr(s string) string { return xmlAttrEscaper.Replace(s) }
+func escapeXMLText(s string) string { return xmlTextEscaper.Replace(s) }
// getSuggestedCustomFields generates suggested custom fields for a document using the LLM
func (app *App) getSuggestedCustomFields(ctx context.Context, doc Document, selectedFieldIDs []int, logger *logrus.Entry) ([]CustomFieldSuggestion, error) {
// Fetch all available custom fields
@@ -402,13 +417,13 @@ func (app *App) getSuggestedCustomFields(ctx context.Context, doc Document, sele
xmlBuilder.WriteString("\n")
for _, field := range selectedCustomFields {
if field.DataType == "select" && field.ExtraData != nil && len(field.ExtraData.SelectOptions) > 0 {
- xmlBuilder.WriteString(fmt.Sprintf(" \n", field.Name, field.DataType))
+ xmlBuilder.WriteString(fmt.Sprintf(" \n", escapeXMLAttr(field.Name), escapeXMLAttr(field.DataType)))
for _, opt := range field.ExtraData.SelectOptions {
- xmlBuilder.WriteString(fmt.Sprintf(" \n", opt.ID, opt.Label))
+ xmlBuilder.WriteString(fmt.Sprintf(" \n", escapeXMLAttr(opt.ID), escapeXMLText(opt.Label)))
}
xmlBuilder.WriteString(" \n")
} else {
- xmlBuilder.WriteString(fmt.Sprintf(" \n", field.Name, field.DataType))
+ xmlBuilder.WriteString(fmt.Sprintf(" \n", escapeXMLAttr(field.Name), escapeXMLAttr(field.DataType)))
}
}
xmlBuilder.WriteString("")