From f92c861e2d89e847f32e23c931383c8b3c944079 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 2 Sep 2026 13:48:30 -0700 Subject: [PATCH] Enhancement: allow disabling auto-suggestions for inbox documents --- docs/advanced_usage.md | 4 ++- docs/usage.md | 2 ++ .../admin/settings/settings.component.html | 6 ++++ .../admin/settings/settings.component.spec.ts | 2 +- .../admin/settings/settings.component.ts | 8 +++++ .../document-detail.component.spec.ts | 29 +++++++++++++++++++ .../document-detail.component.ts | 8 +++++ src-ui/src/app/data/ui-settings.ts | 7 +++++ 8 files changed, 64 insertions(+), 2 deletions(-) diff --git a/docs/advanced_usage.md b/docs/advanced_usage.md index 29c427fb0..661c2d704 100644 --- a/docs/advanced_usage.md +++ b/docs/advanced_usage.md @@ -138,7 +138,9 @@ for suggested generation and embedding models. With AI enabled, Paperless-ngx can suggest a title, tags, correspondent, document type, storage path and dates by sending the document to the LLM. This is **opt-in per request** and surfaces through the "Suggest" control on the document detail page, alongside the -classic classifier-based suggestions — it does not disable them. Suggestion output +classic classifier-based suggestions — it does not disable them. Suggestions are requested +automatically when you open a document that carries an inbox tag unless "Automatically request +suggestions for inbox documents" under Settings > Documents is disabled. Suggestion output language can be steered with [`PAPERLESS_AI_LLM_OUTPUT_LANGUAGE`](configuration.md#PAPERLESS_AI_LLM_OUTPUT_LANGUAGE) (otherwise it follows the user's UI language). diff --git a/docs/usage.md b/docs/usage.md index 805e15718..cc5811ba5 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -317,6 +317,8 @@ a "document already exists" message. Paperless-ngx can suggest tags, correspondents, document types and storage paths for documents based on the content of the document. This is done using a (non-LLM) machine learning model that is trained on the documents in your database. The suggestions are shown in the document detail page and can be accepted or rejected by the user. +Suggestions are requested automatically when you open a document that still has an inbox tag. To only request them by pressing the "Suggest" button instead, turn off "Automatically request suggestions for inbox documents" under Settings > Documents. This applies to both classifier-based and AI suggestions. + ## AI Features Paperless-ngx includes several features that use AI to enhance the document management experience. These features are optional and can be enabled or disabled in the settings. If you are using the AI features, you may want to also enable the "LLM index" feature, which supports Retrieval-Augmented Generation (RAG) designed to improve the quality of AI responses. The LLM index feature is not enabled by default and requires additional configuration. diff --git a/src-ui/src/app/components/admin/settings/settings.component.html b/src-ui/src/app/components/admin/settings/settings.component.html index 569815d35..82906e9c5 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.html +++ b/src-ui/src/app/components/admin/settings/settings.component.html @@ -237,6 +237,12 @@ +
+
+ +
+
+
diff --git a/src-ui/src/app/components/admin/settings/settings.component.spec.ts b/src-ui/src/app/components/admin/settings/settings.component.spec.ts index 7d24063b4..7b65281d0 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.spec.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.spec.ts @@ -267,7 +267,7 @@ describe('SettingsComponent', () => { expect(toastErrorSpy).toHaveBeenCalled() expect(storeSpy).toHaveBeenCalled() expect(appearanceSettingsSpy).not.toHaveBeenCalled() - expect(setSpy).toHaveBeenCalledTimes(32) + expect(setSpy).toHaveBeenCalledTimes(33) // succeed storeSpy.mockReturnValueOnce(of(true)) diff --git a/src-ui/src/app/components/admin/settings/settings.component.ts b/src-ui/src/app/components/admin/settings/settings.component.ts index b9409fe06..218f6683c 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.ts @@ -168,6 +168,7 @@ export class SettingsComponent pdfEditorDefaultEditMode: new FormControl(null), documentEditingRemoveInboxTags: new FormControl(null), documentEditingOverlayThumbnail: new FormControl(null), + documentEditingAutoSuggest: new FormControl(null), documentDetailsHiddenFields: new FormControl([]), searchDbOnly: new FormControl(null), searchLink: new FormControl(null), @@ -368,6 +369,9 @@ export class SettingsComponent documentEditingOverlayThumbnail: this.settings.get( SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL ), + documentEditingAutoSuggest: this.settings.get( + SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST + ), documentDetailsHiddenFields: this.settings.get( SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS ), @@ -565,6 +569,10 @@ export class SettingsComponent SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL, this.settingsForm.value.documentEditingOverlayThumbnail ) + this.settings.set( + SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST, + this.settingsForm.value.documentEditingAutoSuggest + ) this.settings.set( SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS, this.settingsForm.value.documentDetailsHiddenFields diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts index bb75f8d07..58322cfd5 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts @@ -1473,6 +1473,35 @@ describe('DocumentDetailComponent', () => { }) }) + it('should not automatically get suggestions if auto-suggest is disabled', () => { + settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST, false) + const suggestionsSpy = jest.spyOn(documentService, 'getSuggestions') + suggestionsSpy.mockReturnValue(of({ tags: [42] })) + initNormally() + expect(suggestionsSpy).not.toHaveBeenCalled() + + // still available on demand + component.getSuggestions() + expect(suggestionsSpy).toHaveBeenCalled() + }) + + it('should not automatically get AI suggestions if auto-suggest is disabled', () => { + settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST, false) + const getSetting = settingsService.get.bind(settingsService) + jest + .spyOn(settingsService, 'get') + .mockImplementation((key) => + key === SETTINGS_KEYS.AI_ENABLED ? true : getSetting(key) + ) + const aiSuggestionsSpy = jest.spyOn(documentService, 'getAiSuggestions') + aiSuggestionsSpy.mockReturnValue(of({ tags: [42] })) + initNormally() + expect(aiSuggestionsSpy).not.toHaveBeenCalled() + + component.getSuggestions() + expect(aiSuggestionsSpy).toHaveBeenCalled() + }) + it('should reset the suggestions loading state if the document changes mid-request', () => { const getSetting = settingsService.get.bind(settingsService) jest diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index 18478ea40..f1f3188d0 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -237,6 +237,9 @@ export class DocumentDetailComponent this.settings.getSignal( SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL ) + private readonly autoSuggestSetting = this.settings.getSignal( + SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST + ) private readonly hiddenFieldsSetting = this.settings.getSignal< DocumentDetailFieldID[] >(SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS) @@ -357,6 +360,10 @@ export class DocumentDetailComponent return this.aiEnabledSetting() } + get autoSuggest(): boolean { + return this.autoSuggestSetting() + } + get archiveContentRenderType(): ContentRenderType { const hasArchiveVersion = this.metadata()?.has_archive_version ?? @@ -904,6 +911,7 @@ export class DocumentDetailComponent this.updateFormForCustomFields() this.loadMetadataForSelectedVersion() if ( + this.autoSuggest && this.permissionsService.currentUserHasObjectPermissions( PermissionAction.Change, doc diff --git a/src-ui/src/app/data/ui-settings.ts b/src-ui/src/app/data/ui-settings.ts index 4739051fa..959a30669 100644 --- a/src-ui/src/app/data/ui-settings.ts +++ b/src-ui/src/app/data/ui-settings.ts @@ -84,6 +84,8 @@ export const SETTINGS_KEYS = { 'general-settings:document-editing:remove-inbox-tags', DOCUMENT_EDITING_OVERLAY_THUMBNAIL: 'general-settings:document-editing:overlay-thumbnail', + DOCUMENT_EDITING_AUTO_SUGGEST: + 'general-settings:document-editing:auto-suggest', DOCUMENT_DETAILS_HIDDEN_FIELDS: 'general-settings:document-details:hidden-fields', SEARCH_DB_ONLY: 'general-settings:search:db-only', @@ -300,6 +302,11 @@ export const SETTINGS: UiSetting[] = [ type: 'boolean', default: true, }, + { + key: SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST, + type: 'boolean', + default: true, + }, { key: SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS, type: 'array',