Chore: Import paperless_ai helpers lazily in views

This commit is contained in:
Trenton H committed 2026-10-30 10:22:02 -07:00
1 parent 943dc7af99
commit 984607ee42
8 files changed
+43 -39

No files matched your search

+5 -5
View File
@@ -1070,7 +1070,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless.views.llm_index_exists") as mock_exists,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists,
):
mock_exists.return_value = False
self.client.patch(
@@ -1095,7 +1095,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless.views.llm_index_exists", return_value=False),
patch("paperless_ai.indexing.llm_index_exists", return_value=False),
):
self.client.patch(
f"{self.ENDPOINT}1/",
@@ -1115,7 +1115,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless.views.llm_index_exists") as mock_exists,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists,
):
mock_exists.return_value = True
self.client.patch(
@@ -1136,7 +1136,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless.views.llm_index_exists") as mock_exists,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists,
):
mock_exists.return_value = True
self.client.patch(
@@ -1174,7 +1174,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless.views.llm_index_exists") as mock_exists,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists,
):
mock_exists.return_value = True
self.client.patch(
+2 -2
View File
@@ -61,7 +61,7 @@ class TestChatStreamingViewInputValidation(APITestCase):
return_value=self._mock_ai_enabled(),
),
mock.patch(
"documents.views.stream_chat_with_documents",
"paperless_ai.chat.stream_chat_with_documents",
return_value=iter(chunks),
),
):
@@ -104,7 +104,7 @@ class TestChatStreamingViewUnrestrictedFlag:
"""
mocker.patch("documents.views.AIConfig").return_value.ai_enabled = True
return mocker.patch(
"documents.views.stream_chat_with_documents",
"paperless_ai.chat.stream_chat_with_documents",
return_value=iter(()),
)
@@ -1110,7 +1110,7 @@ class TestVersionActionPermissions(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, 403)
with (
mock.patch("documents.views.AIConfig") as ai_config,
mock.patch("documents.views.stream_chat_with_documents") as chat,
mock.patch("paperless_ai.chat.stream_chat_with_documents") as chat,
):
ai_config.return_value.ai_enabled = True
response = self.client.post(
+1 -1
View File
@@ -2686,7 +2686,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
response = self.client.get("/api/documents/34676/suggestions/")
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@mock.patch("documents.views.get_ai_document_classification")
@mock.patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(AI_ENABLED=True)
def test_suggestions_still_uses_classifier_when_ai_enabled(
self,
@@ -572,7 +572,7 @@ class TestAiChatAllDocumentsPermissionBoundary:
ENDPOINT = "/api/documents/chat/"
@override_settings(AI_ENABLED=True)
@patch("documents.views.stream_chat_with_documents")
@patch("paperless_ai.chat.stream_chat_with_documents")
def test_chat_all_documents_excludes_unshared_document(self, mock_stream_chat):
mock_stream_chat.return_value = iter([b"data"])
+16 -16
View File
@@ -474,7 +474,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
self.assertEqual(response.json()["tags"], [])
self.assertEqual(response.json()["suggested_tags"], [])
@patch("documents.views.get_ai_document_classification")
@patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(
AI_ENABLED=True,
LLM_BACKEND="mock_backend",
@@ -524,7 +524,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
None,
)
@patch("documents.views.get_ai_document_classification")
@patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(
AI_ENABLED=True,
LLM_BACKEND="mock_backend",
@@ -562,7 +562,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
"KI Title",
)
@patch("documents.views.get_ai_document_classification")
@patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(
AI_ENABLED=True,
LLM_BACKEND="mock_backend",
@@ -601,7 +601,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
"Titre IA",
)
@patch("documents.views.get_ai_document_classification")
@patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(
AI_ENABLED=True,
LLM_BACKEND="mock_backend",
@@ -637,7 +637,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
),
)
@patch("documents.views.get_ai_document_classification")
@patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(
AI_ENABLED=True,
LLM_BACKEND="mock_backend",
@@ -709,7 +709,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
),
)
@patch("documents.views.get_ai_document_classification")
@patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(
AI_ENABLED=True,
LLM_BACKEND="openai-like",
@@ -738,7 +738,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
get_llm_suggestion_cache(self.document.pk, backend="openai-like"),
)
@patch("documents.views.get_ai_document_classification")
@patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(
AI_ENABLED=True,
LLM_BACKEND="openai-like",
@@ -765,7 +765,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
get_llm_suggestion_cache(self.document.pk, backend="openai-like"),
)
@patch("documents.views.get_ai_document_classification")
@patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(
AI_ENABLED=True,
LLM_BACKEND="openai-like",
@@ -797,7 +797,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
get_llm_suggestion_cache(self.document.pk, backend="openai-like"),
)
@patch("documents.views.get_ai_document_classification")
@patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(
AI_ENABLED=True,
LLM_BACKEND="mock_backend",
@@ -835,7 +835,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
self.assertEqual(response.json()["tags"], [self.tag1.pk])
self.assertEqual(response.json()["suggested_tags"], ["Follow-up"])
@patch("documents.views.get_ai_document_classification")
@patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(
AI_ENABLED=True,
LLM_BACKEND="mock_backend",
@@ -874,7 +874,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
self.assertEqual(response.json()["tags"], [self.tag1.pk])
self.assertEqual(response.json()["suggested_tags"], [])
@patch("documents.views.get_ai_document_classification")
@patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(
AI_ENABLED=True,
LLM_BACKEND="mock_backend",
@@ -996,7 +996,7 @@ class TestAIChatStreamingView(DirectoriesMixin, TestCase):
self.assertEqual(response.status_code, 400)
self.assertIn(b"AI is required for this feature", response.content)
@patch("documents.views.stream_chat_with_documents")
@patch("paperless_ai.chat.stream_chat_with_documents")
@patch("documents.views.permitted_document_ids")
@override_settings(AI_ENABLED=True)
def test_post_no_document_id(self, mock_permitted_ids, mock_stream_chat) -> None:
@@ -1016,7 +1016,7 @@ class TestAIChatStreamingView(DirectoriesMixin, TestCase):
self.assertEqual(list(call_kwargs["documents"]), [self.document])
self.assertIsNone(call_kwargs["output_language"])
@patch("documents.views.stream_chat_with_documents")
@patch("paperless_ai.chat.stream_chat_with_documents")
@patch("documents.views.permitted_document_ids")
@override_settings(AI_ENABLED=True)
def test_post_uses_user_display_language(
@@ -1042,7 +1042,7 @@ class TestAIChatStreamingView(DirectoriesMixin, TestCase):
self.assertEqual(list(call_kwargs["documents"]), [self.document])
self.assertEqual(call_kwargs["output_language"], "de-de")
@patch("documents.views.stream_chat_with_documents")
@patch("paperless_ai.chat.stream_chat_with_documents")
@override_settings(AI_ENABLED=True)
def test_post_with_document_id(self, mock_stream_chat) -> None:
self.grant_view_document_permission()
@@ -1079,7 +1079,7 @@ class TestAIChatStreamingView(DirectoriesMixin, TestCase):
self.assertEqual(response.status_code, 403)
self.assertIn(b"Insufficient permissions", response.content)
@patch("documents.views.stream_chat_with_documents")
@patch("paperless_ai.chat.stream_chat_with_documents")
@override_settings(AI_ENABLED=True)
def test_post_no_document_id_requires_view_document_permission(
self,
@@ -1093,7 +1093,7 @@ class TestAIChatStreamingView(DirectoriesMixin, TestCase):
self.assertEqual(response.status_code, 403)
mock_stream_chat.assert_not_called()
@patch("documents.views.stream_chat_with_documents")
@patch("paperless_ai.chat.stream_chat_with_documents")
@override_settings(AI_ENABLED=True)
def test_post_with_document_id_requires_view_document_permission(
self,
+15 -12
View File
@@ -250,20 +250,8 @@ from paperless.parsers.remote import RemoteEngineConfig
from paperless.serialisers import GroupSerializer
from paperless.serialisers import UserSerializer
from paperless.views import StandardPagination
from paperless_ai.ai_classifier import get_ai_document_classification
from paperless_ai.ai_classifier import get_llm_output_language
from paperless_ai.chat import stream_chat_with_documents
from paperless_ai.exceptions import LLMProviderError
from paperless_ai.exceptions import LLMTimeoutError
from paperless_ai.matching import extract_unmatched_names
from paperless_ai.matching import match_correspondents_by_name
from paperless_ai.matching import match_document_types_by_name
from paperless_ai.matching import match_storage_paths_by_name
from paperless_ai.matching import match_tags_by_name
from paperless_ai.matching import resolve_correspondent_ids
from paperless_ai.matching import resolve_document_type_ids
from paperless_ai.matching import resolve_storage_path_ids
from paperless_ai.matching import resolve_tag_ids
from paperless_mail.models import MailAccount
from paperless_mail.models import MailRule
from paperless_mail.oauth import PaperlessMailOAuth2Manager
@@ -1613,6 +1601,18 @@ class DocumentViewSet(
if not ai_config.ai_enabled:
return HttpResponseBadRequest("AI is required for this feature")
from paperless_ai.ai_classifier import get_ai_document_classification
from paperless_ai.ai_classifier import get_llm_output_language
from paperless_ai.matching import extract_unmatched_names
from paperless_ai.matching import match_correspondents_by_name
from paperless_ai.matching import match_document_types_by_name
from paperless_ai.matching import match_storage_paths_by_name
from paperless_ai.matching import match_tags_by_name
from paperless_ai.matching import resolve_correspondent_ids
from paperless_ai.matching import resolve_document_type_ids
from paperless_ai.matching import resolve_storage_path_ids
from paperless_ai.matching import resolve_tag_ids
output_language = get_llm_output_language(
ai_config=ai_config,
user=request.user,
@@ -2446,6 +2446,9 @@ class ChatStreamingView(GenericAPIView[Any]):
)
unrestricted = user_is_unrestricted(request.user)
from paperless_ai.ai_classifier import get_llm_output_language
from paperless_ai.chat import stream_chat_with_documents
output_language = get_llm_output_language(
ai_config=ai_config,
user=request.user,
+2 -1
View File
@@ -49,7 +49,6 @@ from paperless.serialisers import GroupSerializer
from paperless.serialisers import PaperlessAuthTokenSerializer
from paperless.serialisers import ProfileSerializer
from paperless.serialisers import UserSerializer
from paperless_ai.indexing import llm_index_exists
class PaperlessObtainAuthTokenView(ObtainAuthToken):
@@ -471,6 +470,8 @@ class ApplicationConfigurationViewSet(ModelViewSet[ApplicationConfiguration]):
or old_llm_embedding_model != new_llm_embedding_model
or old_llm_context_size != new_llm_context_size
)
from paperless_ai.indexing import llm_index_exists
rebuild_needed = new_ai_index_enabled and (
not llm_index_exists() or embedding_config_changed
)