django-spire 0.20.0__py3-none-any.whl → 0.20.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_spire/ai/chat/intelligence/decoders/tools.py +7 -2
- django_spire/ai/chat/intelligence/workflows/chat_workflow.py +22 -22
- django_spire/consts.py +1 -1
- django_spire/notification/email/helper.py +8 -0
- django_spire/notification/email/migrations/0003_emailnotification_attachments.py +19 -0
- django_spire/notification/email/models.py +14 -1
- django_spire/notification/email/processor.py +2 -2
- django_spire/notification/models.py +1 -1
- django_spire/settings.py +1 -0
- {django_spire-0.20.0.dist-info → django_spire-0.20.2.dist-info}/METADATA +2 -2
- {django_spire-0.20.0.dist-info → django_spire-0.20.2.dist-info}/RECORD +14 -13
- {django_spire-0.20.0.dist-info → django_spire-0.20.2.dist-info}/WHEEL +0 -0
- {django_spire-0.20.0.dist-info → django_spire-0.20.2.dist-info}/licenses/LICENSE.md +0 -0
- {django_spire-0.20.0.dist-info → django_spire-0.20.2.dist-info}/top_level.txt +0 -0
|
@@ -5,6 +5,8 @@ from typing import Callable, TYPE_CHECKING
|
|
|
5
5
|
from dandy import Decoder
|
|
6
6
|
|
|
7
7
|
from django_spire.auth.controller.controller import AppAuthController
|
|
8
|
+
from django_spire.conf import settings
|
|
9
|
+
from django_spire.core.utils import get_callable_from_module_string_and_validate_arguments
|
|
8
10
|
from django_spire.knowledge.intelligence.workflows.knowledge_workflow import knowledge_search_workflow
|
|
9
11
|
|
|
10
12
|
if TYPE_CHECKING:
|
|
@@ -20,8 +22,11 @@ def generate_intent_decoder(
|
|
|
20
22
|
if AppAuthController(app_name='knowledge', request=request).can_view():
|
|
21
23
|
intent_dict['The user is looking for information or knowledge on something.'] = knowledge_search_workflow
|
|
22
24
|
|
|
23
|
-
if
|
|
24
|
-
intent_dict['None of the above choices match the user\'s intent'] =
|
|
25
|
+
if settings.AI_CHAT_DEFAULT_CALLABLE is not None:
|
|
26
|
+
intent_dict['None of the above choices match the user\'s intent'] = get_callable_from_module_string_and_validate_arguments(
|
|
27
|
+
settings.AI_CHAT_DEFAULT_CALLABLE,
|
|
28
|
+
['request', 'user_input', 'message_history']
|
|
29
|
+
)
|
|
25
30
|
|
|
26
31
|
return Decoder(
|
|
27
32
|
mapping_keys_description='Intent of the User\'s Request',
|
|
@@ -8,25 +8,15 @@ from dandy.recorder import recorder_to_html_file
|
|
|
8
8
|
from django_spire.ai.chat.intelligence.decoders.tools import generate_intent_decoder
|
|
9
9
|
from django_spire.ai.chat.message_intel import BaseMessageIntel, DefaultMessageIntel
|
|
10
10
|
from django_spire.ai.decorators import log_ai_interaction_from_recorder
|
|
11
|
+
from django_spire.conf import settings
|
|
12
|
+
from django_spire.core.utils import get_callable_from_module_string_and_validate_arguments
|
|
11
13
|
|
|
12
14
|
if TYPE_CHECKING:
|
|
13
15
|
from dandy.llm.request.message import MessageHistory
|
|
14
16
|
from django.core.handlers.wsgi import WSGIRequest
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
def
|
|
18
|
-
request: WSGIRequest,
|
|
19
|
-
user_input: str,
|
|
20
|
-
message_history: MessageHistory | None = None
|
|
21
|
-
) -> BaseMessageIntel:
|
|
22
|
-
return chat_workflow(
|
|
23
|
-
request=request,
|
|
24
|
-
user_input=user_input,
|
|
25
|
-
message_history=message_history
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def default_chat_response(
|
|
19
|
+
def default_chat_callable(
|
|
30
20
|
request: WSGIRequest,
|
|
31
21
|
user_input: str,
|
|
32
22
|
message_history: MessageHistory | None = None
|
|
@@ -45,13 +35,6 @@ def chat_workflow(
|
|
|
45
35
|
user_input: str,
|
|
46
36
|
message_history: MessageHistory | None = None
|
|
47
37
|
) -> BaseMessageIntel:
|
|
48
|
-
intent_decoder = generate_intent_decoder(
|
|
49
|
-
request=request,
|
|
50
|
-
default_callable=default_chat_response,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
intent_process = intent_decoder.process(user_input, max_return_values=1)[0]
|
|
54
|
-
|
|
55
38
|
@log_ai_interaction_from_recorder(request.user)
|
|
56
39
|
def run_workflow_process(callable_: Callable) -> BaseMessageIntel | None:
|
|
57
40
|
return callable_(
|
|
@@ -60,11 +43,28 @@ def chat_workflow(
|
|
|
60
43
|
message_history=message_history,
|
|
61
44
|
)
|
|
62
45
|
|
|
63
|
-
|
|
46
|
+
if settings.AI_CHAT_CALLABLE is not None:
|
|
47
|
+
chat_callable = get_callable_from_module_string_and_validate_arguments(
|
|
48
|
+
settings.AI_CHAT_CALLABLE,
|
|
49
|
+
['request', 'user_input', 'message_history']
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
message_intel = run_workflow_process(chat_callable)
|
|
53
|
+
|
|
54
|
+
else:
|
|
55
|
+
|
|
56
|
+
intent_decoder = generate_intent_decoder(
|
|
57
|
+
request=request,
|
|
58
|
+
default_callable=default_chat_callable,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
intent_process = intent_decoder.process(user_input, max_return_values=1)[0]
|
|
62
|
+
|
|
63
|
+
message_intel = run_workflow_process(intent_process)
|
|
64
64
|
|
|
65
65
|
if not isinstance(message_intel, BaseMessageIntel):
|
|
66
66
|
if message_intel is None:
|
|
67
|
-
return
|
|
67
|
+
return default_chat_callable(
|
|
68
68
|
request=request,
|
|
69
69
|
user_input=user_input,
|
|
70
70
|
message_history=message_history
|
django_spire/consts.py
CHANGED
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from django.conf import settings
|
|
4
4
|
from django.core.mail import EmailMessage
|
|
5
5
|
|
|
6
|
+
from django_spire.file.models import File
|
|
6
7
|
from django_spire.notification.models import Notification
|
|
7
8
|
|
|
8
9
|
|
|
@@ -27,6 +28,11 @@ class EmailHelper:
|
|
|
27
28
|
self.from_email = settings.DEFAULT_FROM_EMAIL
|
|
28
29
|
self.fail_silently = fail_silently
|
|
29
30
|
|
|
31
|
+
self.attachments = []
|
|
32
|
+
for attachment in list(email.attachments.all()):
|
|
33
|
+
with attachment.file.open('rb') as f:
|
|
34
|
+
self.attachments.append((attachment.name, f.read(), attachment.type))
|
|
35
|
+
|
|
30
36
|
|
|
31
37
|
class SendGridEmailHelper(EmailHelper):
|
|
32
38
|
def __init__(
|
|
@@ -58,7 +64,9 @@ class SendGridEmailHelper(EmailHelper):
|
|
|
58
64
|
to=self.to,
|
|
59
65
|
cc=self.cc,
|
|
60
66
|
bcc=self.bcc,
|
|
67
|
+
attachments=self.attachments
|
|
61
68
|
)
|
|
69
|
+
|
|
62
70
|
msg.template_id = self.template_id
|
|
63
71
|
msg.dynamic_template_data = self.template_data
|
|
64
72
|
msg.send(fail_silently=self.fail_silently)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Generated by Django 5.1.8 on 2025-11-13 20:22
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('django_spire_file', '0001_initial'),
|
|
10
|
+
('django_spire_notification_email', '0002_emailnotification_bcc_emailnotification_cc_and_more'),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AddField(
|
|
15
|
+
model_name='emailnotification',
|
|
16
|
+
name='attachments',
|
|
17
|
+
field=models.ManyToManyField(blank=True, related_name='attachments', related_query_name='attachment', to='django_spire_file.file'),
|
|
18
|
+
),
|
|
19
|
+
]
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
from django.db import models
|
|
2
2
|
|
|
3
|
-
from django_spire.
|
|
3
|
+
from django_spire.file.models import File
|
|
4
4
|
from django_spire.notification.email.querysets import EmailNotificationQuerySet
|
|
5
|
+
from django_spire.notification.models import Notification
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class EmailNotification(models.Model):
|
|
9
|
+
"""
|
|
10
|
+
It is important to note size limits for email content contained in this model. E.g., Sendgrid has a hard total email
|
|
11
|
+
limit of 30mb (and a recommended limit of 10mb for attachments): https://www.twilio.com/docs/sendgrid/ui/sending-email/attachments-with-digioh#-Limitations
|
|
12
|
+
"""
|
|
8
13
|
notification = models.OneToOneField(
|
|
9
14
|
Notification,
|
|
10
15
|
editable=False,
|
|
@@ -12,6 +17,14 @@ class EmailNotification(models.Model):
|
|
|
12
17
|
related_name='email',
|
|
13
18
|
related_query_name='email',
|
|
14
19
|
)
|
|
20
|
+
|
|
21
|
+
attachments = models.ManyToManyField(
|
|
22
|
+
File,
|
|
23
|
+
blank=True,
|
|
24
|
+
related_name='attachments',
|
|
25
|
+
related_query_name='attachment',
|
|
26
|
+
)
|
|
27
|
+
|
|
15
28
|
to_email_address = models.EmailField()
|
|
16
29
|
template_id = models.CharField(max_length=64, default='')
|
|
17
30
|
context_data = models.JSONField(default=dict)
|
|
@@ -73,7 +73,7 @@ class EmailNotificationProcessor(BaseNotificationProcessor):
|
|
|
73
73
|
email_notifications()
|
|
74
74
|
.ready_to_send()
|
|
75
75
|
.active()
|
|
76
|
-
.prefetch_related('email')
|
|
76
|
+
.prefetch_related('email', 'email__attachment')
|
|
77
77
|
)
|
|
78
78
|
|
|
79
79
|
def process_errored(self):
|
|
@@ -82,5 +82,5 @@ class EmailNotificationProcessor(BaseNotificationProcessor):
|
|
|
82
82
|
.email_notifications()
|
|
83
83
|
.errored()
|
|
84
84
|
.active()
|
|
85
|
-
.prefetch_related('email')
|
|
85
|
+
.prefetch_related('email', 'email__attachment')
|
|
86
86
|
)
|
|
@@ -8,7 +8,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
|
|
|
8
8
|
|
|
9
9
|
from django_spire.history.mixins import HistoryModelMixin
|
|
10
10
|
from django_spire.notification.choices import (
|
|
11
|
-
NotificationTypeChoices, NotificationPriorityChoices, NotificationStatusChoices
|
|
11
|
+
NotificationTypeChoices, NotificationPriorityChoices, NotificationStatusChoices,
|
|
12
12
|
)
|
|
13
13
|
from django_spire.notification.querysets import NotificationQuerySet
|
|
14
14
|
|
django_spire/settings.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-spire
|
|
3
|
-
Version: 0.20.
|
|
3
|
+
Version: 0.20.2
|
|
4
4
|
Summary: A project for Django Spire
|
|
5
5
|
Author-email: Brayden Carlson <braydenc@stratusadv.com>, Nathan Johnson <nathanj@stratusadv.com>
|
|
6
6
|
License: Copyright (c) 2024 Stratus Advanced Technologies and Contributors.
|
|
@@ -49,7 +49,7 @@ Requires-Dist: beautifulsoup4>=4.14.2
|
|
|
49
49
|
Requires-Dist: boto3>=1.34.0
|
|
50
50
|
Requires-Dist: botocore>=1.34.0
|
|
51
51
|
Requires-Dist: crispy-bootstrap5==2024.10
|
|
52
|
-
Requires-Dist: dandy>=1.3.
|
|
52
|
+
Requires-Dist: dandy>=1.3.3
|
|
53
53
|
Requires-Dist: django>=5.1.8
|
|
54
54
|
Requires-Dist: django-crispy-forms==2.3
|
|
55
55
|
Requires-Dist: django-glue>=0.8.1
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
django_spire/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
django_spire/conf.py,sha256=c5Hs-7lk9T15254tOasiQ2ZTFLQIVJof9_QJDfm1PAI,933
|
|
3
|
-
django_spire/consts.py,sha256=
|
|
3
|
+
django_spire/consts.py,sha256=4gTrojrEZVyMXC1XkjBgNQ0x3NXM6MSEIRWwdYAcLoI,171
|
|
4
4
|
django_spire/exceptions.py,sha256=L5ndRO5ftMmh0pHkO2z_NG3LSGZviJ-dDHNT73SzTNw,48
|
|
5
|
-
django_spire/settings.py,sha256=
|
|
5
|
+
django_spire/settings.py,sha256=5gzQCMKAHcBYFREPrYLkQcqFj5gSSgyduUU1H2ztWN8,685
|
|
6
6
|
django_spire/urls.py,sha256=mKeZszb5U4iIGqddMb5Tt5fRC72U2wABEOi6mvOfEBU,656
|
|
7
7
|
django_spire/utils.py,sha256=kW0HP1xWj8Oz0h1GWs4NflnD8Jq8_F4hABwKTiT-Iyk,1006
|
|
8
8
|
django_spire/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -25,9 +25,9 @@ django_spire/ai/chat/auth/controller.py,sha256=l4xHQxSlPgS_QPvofsARUOMcXzVo3cTMs
|
|
|
25
25
|
django_spire/ai/chat/intelligence/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
django_spire/ai/chat/intelligence/prompts.py,sha256=jnFaN7EUUQM-wxloWJZ2UAGw1RDXIOKkX4JfvT6ukxw,686
|
|
27
27
|
django_spire/ai/chat/intelligence/decoders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
django_spire/ai/chat/intelligence/decoders/tools.py,sha256=
|
|
28
|
+
django_spire/ai/chat/intelligence/decoders/tools.py,sha256=pC3Rvg0a1bgi0ESAsDH2HDJQTsu9aCcTslXfp4R7HwQ,1233
|
|
29
29
|
django_spire/ai/chat/intelligence/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
-
django_spire/ai/chat/intelligence/workflows/chat_workflow.py,sha256=
|
|
30
|
+
django_spire/ai/chat/intelligence/workflows/chat_workflow.py,sha256=VaaIqN2U5C1opyQXyJevDs9fDtfwn7gVm5-EZDju1EI,2475
|
|
31
31
|
django_spire/ai/chat/migrations/0001_initial.py,sha256=1cbREhX3_fNsbfumJoKAZ8w91Kq5NeXUn_iI45B7oGE,2632
|
|
32
32
|
django_spire/ai/chat/migrations/0002_remove_chatmessage_content_chatmessage__content_and_more.py,sha256=KeNT7VRFmwA74odh9wxIE1Cr4KAO4Tmtqu0FuI2AmK0,752
|
|
33
33
|
django_spire/ai/chat/migrations/0003_rename__content_chatmessage__intel_data_and_more.py,sha256=wAcJ6Ia3fWrGbqnVrqD2C3-3ijAot0LK-B3KZavoY_A,754
|
|
@@ -1007,7 +1007,7 @@ django_spire/notification/exceptions.py,sha256=gD_rJkZ0t6Pn13FJqf9p5uT0QWEJ58igy
|
|
|
1007
1007
|
django_spire/notification/managers.py,sha256=QzRIRRffua5yl71RDQzGQPk7FHN9UfVMwX2aWuf2MbQ,1595
|
|
1008
1008
|
django_spire/notification/maps.py,sha256=iTZnuro23HX4tg069FS148sZY9CbVlKZ83Jvh0WiIBI,622
|
|
1009
1009
|
django_spire/notification/mixins.py,sha256=deGNC_SD7P9NfM2exFvbZlpOF_daMTW9zzeWre0zXfA,294
|
|
1010
|
-
django_spire/notification/models.py,sha256=
|
|
1010
|
+
django_spire/notification/models.py,sha256=51DxAW0XeBSKx7BhPKQ-YabG1VzoKgJ_EoxhFd3-UL8,2140
|
|
1011
1011
|
django_spire/notification/querysets.py,sha256=Tq1UjVTyD3xOMBAsNDyx6BNPS-8rwIGs4JrlnCeg3jM,3860
|
|
1012
1012
|
django_spire/notification/urls.py,sha256=cewnrbbMe5A7zExvYX19BmOJPpcpcKGgpXzGZs5CIJw,249
|
|
1013
1013
|
django_spire/notification/utils.py,sha256=cc2qHoKHv9xUfqkUljN2hPSPUmgnXS30dIez3exlBRI,253
|
|
@@ -1040,12 +1040,13 @@ django_spire/notification/email/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
|
|
|
1040
1040
|
django_spire/notification/email/admin.py,sha256=qpz1nfYx9oy_Bvnkc5wtZktP_7KNzBOjPwIG1s2G7A8,264
|
|
1041
1041
|
django_spire/notification/email/apps.py,sha256=xePF3Rnc2IEWxFF-jO_vNi-ynl-WGQ4yCvX6itfLfaE,419
|
|
1042
1042
|
django_spire/notification/email/exceptions.py,sha256=pzDVfTzIEBlBKCIrSscVWZbIZ1F-xsHpWHA1R2_zF2s,124
|
|
1043
|
-
django_spire/notification/email/helper.py,sha256=
|
|
1044
|
-
django_spire/notification/email/models.py,sha256=
|
|
1045
|
-
django_spire/notification/email/processor.py,sha256=
|
|
1043
|
+
django_spire/notification/email/helper.py,sha256=qwBrphhmrN0RE7nvqUsQq_lGYm5tZ9Mnwfl-5-Gm7Yw,2167
|
|
1044
|
+
django_spire/notification/email/models.py,sha256=aB7y_ZGCZ90i_H4EBpZ-Pea7GCfBuRdlo3oUuHP8lL0,1452
|
|
1045
|
+
django_spire/notification/email/processor.py,sha256=BzrgqpBTazvsqB5KzN64WTfAMmuScjUtGgflvEU1ZpY,3292
|
|
1046
1046
|
django_spire/notification/email/querysets.py,sha256=xXjax8Ce35Fs5nET9Abzw-4J1BIvgUDOn1Xxt8iiLzE,1217
|
|
1047
1047
|
django_spire/notification/email/migrations/0001_initial.py,sha256=o8BObuOTRl3IqqLuEWpYXMs4qh4-7eY3TVD004pWYb0,1029
|
|
1048
1048
|
django_spire/notification/email/migrations/0002_emailnotification_bcc_emailnotification_cc_and_more.py,sha256=M2cafrh7ESAdpUQhfIYziGRAcTLVqjTVwn2A6H3zqq4,907
|
|
1049
|
+
django_spire/notification/email/migrations/0003_emailnotification_attachments.py,sha256=MXQx_8WFD3IMe6bykcowavouoD2aG2VVAOpxjGWK7Ec,597
|
|
1049
1050
|
django_spire/notification/email/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1050
1051
|
django_spire/notification/migrations/0001_initial.py,sha256=0KQFckVRQQvMtMRwQaFwJfcZ6U38rRboJpqlr7DpvgE,2594
|
|
1051
1052
|
django_spire/notification/migrations/0002_pushnotification.py,sha256=5Vlz3V1LycFpZK-HJVA6kJnFPPB27Il_LD6kDBwa1dg,999
|
|
@@ -1142,8 +1143,8 @@ django_spire/theme/urls/page_urls.py,sha256=S8nkKkgbhG3XHI3uMUL-piOjXIrRkuY2UlM_
|
|
|
1142
1143
|
django_spire/theme/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1143
1144
|
django_spire/theme/views/json_views.py,sha256=W1khC2K_EMbEzAFmMxC_P76_MFnkRH4-eVdodrRfAhw,1904
|
|
1144
1145
|
django_spire/theme/views/page_views.py,sha256=pHr8iekjtR99xs7w1taj35HEo133Svq1dvDD0y0VL1c,3933
|
|
1145
|
-
django_spire-0.20.
|
|
1146
|
-
django_spire-0.20.
|
|
1147
|
-
django_spire-0.20.
|
|
1148
|
-
django_spire-0.20.
|
|
1149
|
-
django_spire-0.20.
|
|
1146
|
+
django_spire-0.20.2.dist-info/licenses/LICENSE.md,sha256=tlTbOtgKoy-xAQpUk9gPeh9O4oRXCOzoWdW3jJz0wnA,1091
|
|
1147
|
+
django_spire-0.20.2.dist-info/METADATA,sha256=ONxHK7ThNrmHEG1WHKwcvYBwX_AnPLigNlLbQSRBKjg,4967
|
|
1148
|
+
django_spire-0.20.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
1149
|
+
django_spire-0.20.2.dist-info/top_level.txt,sha256=xf3QV1e--ONkVpgMDQE9iqjQ1Vg4--_6C8wmO-KxPHQ,13
|
|
1150
|
+
django_spire-0.20.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|