django-spire 0.18.2__py3-none-any.whl → 0.19.0__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/admin.py +1 -0
- django_spire/ai/chat/apps.py +1 -8
- django_spire/ai/chat/intelligence/decoders/tools.py +29 -0
- django_spire/ai/chat/intelligence/prompts.py +13 -7
- django_spire/ai/chat/intelligence/workflows/chat_workflow.py +40 -52
- django_spire/ai/chat/models.py +2 -1
- django_spire/ai/chat/querysets.py +1 -3
- django_spire/ai/chat/responses.py +5 -0
- django_spire/ai/chat/templates/django_spire/ai/chat/card/chat_card.html +2 -2
- django_spire/ai/chat/templates/django_spire/ai/chat/dropdown/ellipsis_dropdown.html +1 -1
- django_spire/ai/chat/templates/django_spire/ai/chat/element/recent_chat_select_element.html +82 -44
- django_spire/ai/chat/templates/django_spire/ai/chat/message/loading_response_message.html +8 -3
- django_spire/ai/chat/templates/django_spire/ai/chat/message/message.html +16 -7
- django_spire/ai/chat/templates/django_spire/ai/chat/message/request_message.html +5 -8
- django_spire/ai/chat/templates/django_spire/ai/chat/message/response_message.html +14 -10
- django_spire/ai/chat/templates/django_spire/ai/chat/page/chat_page.html +35 -0
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/dialog_widget.html +72 -23
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/selection_widget.html +42 -0
- django_spire/ai/chat/tests/test_urls/test_json_urls.py +4 -2
- django_spire/ai/chat/urls/__init__.py +1 -1
- django_spire/ai/chat/urls/json_urls.py +1 -1
- django_spire/ai/chat/urls/page_urls.py +1 -1
- django_spire/ai/chat/urls/{template_urls.py → template/template_urls.py} +1 -1
- django_spire/ai/chat/views/json_views.py +4 -3
- django_spire/ai/chat/views/message_request_views.py +32 -14
- django_spire/ai/chat/views/message_response_views.py +14 -11
- django_spire/ai/chat/views/message_views.py +7 -1
- django_spire/ai/chat/views/page_views.py +2 -2
- django_spire/ai/chat/views/{template_views.py → template/template_views.py} +1 -7
- django_spire/ai/context/__init__.py +0 -0
- django_spire/ai/context/admin.py +15 -0
- django_spire/ai/context/apps.py +16 -0
- django_spire/ai/context/choices.py +11 -0
- django_spire/ai/context/intelligence/__init__.py +0 -0
- django_spire/ai/context/intelligence/prompts/__init__.py +0 -0
- django_spire/ai/context/intelligence/prompts/organization_prompts.py +19 -0
- django_spire/ai/context/migrations/0001_initial.py +67 -0
- django_spire/ai/context/migrations/__init__.py +0 -0
- django_spire/ai/context/models.py +67 -0
- django_spire/ai/context/querysets.py +15 -0
- django_spire/ai/context/seeding/__init__.py +0 -0
- django_spire/ai/context/seeding/seed.py +24 -0
- django_spire/ai/prompt/system/bots.py +3 -5
- django_spire/ai/prompt/tuning/bots.py +5 -10
- django_spire/ai/sms/admin.py +2 -0
- django_spire/ai/sms/apps.py +2 -7
- django_spire/ai/sms/decorators.py +2 -2
- django_spire/ai/sms/intelligence/workflows/sms_conversation_workflow.py +18 -18
- django_spire/ai/sms/tests/test_webhook.py +5 -4
- django_spire/ai/sms/urls.py +3 -0
- django_spire/ai/sms/views.py +3 -5
- django_spire/ai/urls.py +2 -0
- django_spire/auth/templates/django_spire/auth/form/login_form.html +25 -0
- django_spire/auth/templates/django_spire/auth/page/auth_page.html +2 -3
- django_spire/auth/templates/django_spire/auth/page/login_page.html +1 -25
- django_spire/conf.py +1 -3
- django_spire/consts.py +1 -7
- django_spire/contrib/seeding/intelligence/bots/seeder_generator_bot.py +3 -2
- django_spire/contrib/utils.py +2 -0
- django_spire/core/static/django_spire/css/app-button.css +11 -1
- django_spire/core/static/django_spire/css/app-import.css +2 -0
- django_spire/core/static/django_spire/css/app-layout.css +15 -0
- django_spire/core/static/django_spire/css/app-navigation.css +24 -2
- django_spire/core/static/django_spire/css/app-page.css +8 -0
- django_spire/core/static/django_spire/css/app-side-panel.css +102 -0
- django_spire/core/static/django_spire/css/app-text.css +19 -1
- django_spire/core/static/django_spire/css/bootstrap-extension.css +128 -7
- django_spire/core/static/django_spire/css/bootstrap-override.css +161 -111
- django_spire/core/static/django_spire/css/themes/ayu/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/ayu/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/catppuccin/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/catppuccin/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/default/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/default/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/dracula/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/dracula/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/gruvbox/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/gruvbox/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/material/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/material/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/nord/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/nord/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/oceanic-next/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/oceanic-next/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/one-dark/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/one-dark/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/palenight/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/palenight/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/rose-pine/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/rose-pine/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/synthwave/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/synthwave/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/tokyo-night/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/tokyo-night/app-light.css +0 -4
- django_spire/core/static/django_spire/font/Poppins-Black.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-BlackItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-Bold.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-BoldItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-ExtraBold.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-ExtraBoldItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-ExtraLight.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-ExtraLightItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-Italic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-Light.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-LightItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-Medium.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-MediumItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-SemiBold.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-SemiBoldItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-Thin.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-ThinItalic.ttf +0 -0
- django_spire/core/static/django_spire/js/ui.js +12 -0
- django_spire/core/templates/django_spire/button/base_button.html +1 -1
- django_spire/core/templates/django_spire/button/primary_dark_outlined_button.html +3 -0
- django_spire/core/templates/django_spire/dropdown/element/dropdown_link_element.html +12 -8
- django_spire/core/templates/django_spire/dropdown/element/ellipsis_dropdown_modal_link_element.html +2 -1
- django_spire/core/templates/django_spire/dropdown/ellipsis_dropdown.html +2 -1
- django_spire/core/templates/django_spire/navigation/accordion/nav_accordion.html +1 -1
- django_spire/core/templates/django_spire/navigation/elements/nav_link.html +1 -1
- django_spire/core/templates/django_spire/navigation/elements/nav_title_divider.html +1 -1
- django_spire/core/templates/django_spire/navigation/side_navigation.html +52 -8
- django_spire/core/templates/django_spire/navigation/top_navigation.html +8 -2
- django_spire/core/templates/django_spire/page/full_page.html +218 -11
- django_spire/core/utils.py +26 -2
- django_spire/knowledge/collection/models.py +24 -0
- django_spire/knowledge/collection/services/transformation_service.py +2 -1
- django_spire/knowledge/collection/tests/test_services/test_transformation_service.py +44 -42
- django_spire/knowledge/collection/urls/form_urls.py +1 -0
- django_spire/knowledge/collection/urls/page_urls.py +1 -0
- django_spire/knowledge/collection/views/form_views.py +13 -2
- django_spire/knowledge/collection/views/page_views.py +36 -4
- django_spire/knowledge/entry/models.py +32 -0
- django_spire/knowledge/entry/services/transformation_services.py +10 -10
- django_spire/knowledge/entry/version/block/data/data.py +2 -1
- django_spire/knowledge/entry/version/block/data/heading_data.py +2 -2
- django_spire/knowledge/entry/version/block/data/list/data.py +3 -4
- django_spire/knowledge/entry/version/block/data/maps.py +3 -5
- django_spire/knowledge/entry/version/block/data/text_data.py +2 -2
- django_spire/knowledge/entry/version/block/models.py +10 -7
- django_spire/knowledge/entry/version/block/services/factory_service.py +11 -10
- django_spire/knowledge/entry/version/block/tests/factories.py +6 -5
- django_spire/knowledge/entry/version/converters/docx_converter.py +2 -4
- django_spire/knowledge/entry/version/intelligence/bots/markdown_format_llm_bot.py +16 -12
- django_spire/knowledge/entry/version/seeding/seeder.py +3 -3
- django_spire/knowledge/entry/version/services/processor_service.py +36 -10
- django_spire/knowledge/entry/version/tests/test_converters/test_docx_converter.py +1 -1
- django_spire/knowledge/entry/version/tests/test_urls/test_page_urls.py +1 -1
- django_spire/knowledge/entry/version/urls/page_urls.py +1 -1
- django_spire/knowledge/entry/version/views/json_views.py +5 -3
- django_spire/knowledge/entry/version/views/page_views.py +11 -12
- django_spire/knowledge/entry/version/views/redirect_views.py +1 -1
- django_spire/knowledge/entry/views/form_views.py +1 -1
- django_spire/knowledge/intelligence/bots/entry_search_llm_bot.py +33 -11
- django_spire/knowledge/intelligence/decoders/__init__.py +0 -0
- django_spire/knowledge/intelligence/{maps/collection_map.py → decoders/collection_decoder.py} +5 -5
- django_spire/knowledge/intelligence/{maps/entry_map.py → decoders/entry_decoder.py} +3 -3
- django_spire/knowledge/intelligence/intel/collection_intel.py +1 -0
- django_spire/knowledge/intelligence/intel/entry_intel.py +5 -2
- django_spire/knowledge/intelligence/intel/message_intel.py +2 -0
- django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +43 -51
- django_spire/knowledge/migrations/0007_alter_collection_options.py +17 -0
- django_spire/knowledge/static/django_spire/knowledge/entry/version/js/editor.js +24 -5
- django_spire/knowledge/templates/django_spire/knowledge/collection/card/top_level_list_card.html +21 -0
- django_spire/knowledge/templates/django_spire/knowledge/collection/component/x_collection_navigation.html +15 -6
- django_spire/knowledge/templates/django_spire/knowledge/collection/element/ellipsis_dropdown.html +22 -0
- django_spire/knowledge/templates/django_spire/knowledge/collection/form/form.html +21 -9
- django_spire/knowledge/templates/django_spire/knowledge/collection/item/collection_item.html +19 -0
- django_spire/knowledge/templates/django_spire/knowledge/collection/page/display_page.html +49 -0
- django_spire/knowledge/templates/django_spire/knowledge/collection/page/form_page.html +1 -1
- django_spire/knowledge/templates/django_spire/knowledge/container/container.html +33 -0
- django_spire/knowledge/templates/django_spire/knowledge/entry/file/page/list_page.html +1 -1
- django_spire/knowledge/templates/django_spire/knowledge/entry/item/list_item.html +2 -2
- django_spire/knowledge/templates/django_spire/knowledge/entry/page/form_page.html +1 -1
- django_spire/knowledge/templates/django_spire/knowledge/entry/page/import_form_page.html +1 -1
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/container/detail_container.html +51 -45
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/page/editor_page.html +54 -0
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/page/form_page.html +1 -1
- django_spire/knowledge/templates/django_spire/knowledge/message/knowledge_message_intel.html +18 -1
- django_spire/knowledge/templates/django_spire/knowledge/page/full_page.html +37 -0
- django_spire/knowledge/templates/django_spire/knowledge/page/home_page.html +2 -2
- django_spire/knowledge/templates/django_spire/knowledge/sub_navigation/item/collection_sub_navigation_item.html +29 -0
- django_spire/knowledge/templates/django_spire/knowledge/sub_navigation/item/entry_sub_navigation_item.html +20 -0
- django_spire/knowledge/templates/django_spire/knowledge/{navigation/content/navigation_content.html → sub_navigation/widget/collection_entry_sub_navigation_widget.html} +42 -19
- django_spire/knowledge/views/page_views.py +4 -4
- django_spire/settings.py +3 -3
- django_spire/theme/templates/django_spire/theme/card/badges_preview_card.html +0 -1
- django_spire/theme/templates/django_spire/theme/card/base_preview_card.html +1 -0
- django_spire/theme/templates/django_spire/theme/card/typography_preview_card.html +1 -1
- django_spire/theme/templates/django_spire/theme/example/form/example_form.html +2 -0
- django_spire/theme/templates/django_spire/theme/example/form/example_form_card.html +5 -0
- django_spire/theme/templates/django_spire/theme/page/badges_page.html +7 -9
- django_spire/theme/templates/django_spire/theme/page/borders_page.html +6 -9
- django_spire/theme/templates/django_spire/theme/page/buttons_page.html +6 -9
- django_spire/theme/templates/django_spire/theme/page/colors_page.html +6 -6
- django_spire/theme/templates/django_spire/theme/page/dashboard_page.html +57 -0
- django_spire/theme/templates/django_spire/theme/page/django_glue_page.html +7 -9
- django_spire/theme/templates/django_spire/theme/page/theme_page.html +24 -0
- django_spire/theme/templates/django_spire/theme/page/typography_page.html +6 -9
- django_spire/theme/templates/django_spire/theme/section/badge_section_card.html +5 -0
- django_spire/theme/templates/django_spire/theme/section/border_section_card.html +5 -0
- django_spire/theme/templates/django_spire/theme/section/button_section.html +1 -0
- django_spire/theme/templates/django_spire/theme/section/button_section_card.html +5 -0
- django_spire/theme/templates/django_spire/theme/section/color_section.html +10 -10
- django_spire/theme/templates/django_spire/theme/section/color_section_card.html +5 -0
- django_spire/theme/templates/django_spire/theme/section/typography_section.html +41 -1
- django_spire/theme/templates/django_spire/theme/section/typography_section_card.html +5 -0
- {django_spire-0.18.2.dist-info → django_spire-0.19.0.dist-info}/METADATA +3 -3
- {django_spire-0.18.2.dist-info → django_spire-0.19.0.dist-info}/RECORD +217 -178
- django_spire/ai/chat/intelligence/bots/chat_bot.py +0 -8
- django_spire/ai/chat/intelligence/maps/intent_llm_map.py +0 -10
- django_spire/ai/chat/templates/django_spire/ai/chat/page/home_page.html +0 -9
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/chat_widget.html +0 -64
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/search_chat_results_widget.html +0 -25
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/search_chat_widget.html +0 -39
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/select_chat_widget.html +0 -24
- django_spire/ai/chat/tools.py +0 -57
- django_spire/ai/sms/tools.py +0 -57
- django_spire/core/static/django_spire/font/Karla-Bold.ttf +0 -0
- django_spire/core/static/django_spire/font/Merriweather.ttf +0 -0
- django_spire/knowledge/context_processors.py +0 -18
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/page/detail_page.html +0 -25
- django_spire/knowledge/templates/django_spire/knowledge/navigation/card/navigation_card.html +0 -9
- django_spire/knowledge/templates/django_spire/knowledge/navigation/item/collection/collection_item.html +0 -29
- django_spire/knowledge/templates/django_spire/knowledge/navigation/item/entry/entry_item.html +0 -18
- django_spire/knowledge/templates/django_spire/knowledge/navigation/page/full_page.html +0 -90
- /django_spire/ai/chat/intelligence/{bots → decoders}/__init__.py +0 -0
- /django_spire/ai/chat/{intelligence/maps → urls/template}/__init__.py +0 -0
- /django_spire/{knowledge/intelligence/maps → ai/chat/views/template}/__init__.py +0 -0
- /django_spire/knowledge/entry/version/{constants.py → consts.py} +0 -0
- /django_spire/knowledge/templates/django_spire/knowledge/{navigation/item/collection/dropdown/navigation_ellipsis_dropdown.html → sub_navigation/element/collection_sub_navigation_ellipsis_dropdown.html} +0 -0
- /django_spire/knowledge/templates/django_spire/knowledge/{navigation/item/entry/dropdown/navigation_ellipsis_dropdown.html → sub_navigation/element/entry_sub_navigation_ellipsis_dropdown.html} +0 -0
- {django_spire-0.18.2.dist-info → django_spire-0.19.0.dist-info}/WHEEL +0 -0
- {django_spire-0.18.2.dist-info → django_spire-0.19.0.dist-info}/licenses/LICENSE.md +0 -0
- {django_spire-0.18.2.dist-info → django_spire-0.19.0.dist-info}/top_level.txt +0 -0
django_spire/ai/admin.py
CHANGED
|
@@ -35,6 +35,7 @@ class AiUsageAdmin(AiUsageAdminMixin):
|
|
|
35
35
|
was_successful: bool | None = None,
|
|
36
36
|
) -> str:
|
|
37
37
|
was_successful_filter = '' if was_successful is None else f'&was_successful__exact={int(was_successful)}'
|
|
38
|
+
|
|
38
39
|
url = (
|
|
39
40
|
reverse("admin:django_spire_ai_aiinteraction_changelist")
|
|
40
41
|
+ "?"
|
django_spire/ai/chat/apps.py
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from django.apps import AppConfig
|
|
4
|
-
from django.conf import settings
|
|
5
|
-
|
|
6
|
-
from django_spire.consts import AI_CHAT_WORKFLOW_CLASS_SETTINGS_NAME
|
|
7
4
|
from django_spire.utils import check_required_apps
|
|
8
5
|
|
|
9
6
|
|
|
@@ -20,11 +17,7 @@ class AiChatConfig(AppConfig):
|
|
|
20
17
|
},
|
|
21
18
|
)
|
|
22
19
|
|
|
23
|
-
REQUIRED_APPS = ('django_spire_ai',)
|
|
20
|
+
REQUIRED_APPS = ('django_spire_ai', 'django_spire_ai_context')
|
|
24
21
|
|
|
25
22
|
def ready(self) -> None:
|
|
26
|
-
if not isinstance(getattr(settings, AI_CHAT_WORKFLOW_CLASS_SETTINGS_NAME), str):
|
|
27
|
-
message = f'"{AI_CHAT_WORKFLOW_CLASS_SETTINGS_NAME}" must be set in the django settings when using "{self.label}".'
|
|
28
|
-
raise TypeError(message)
|
|
29
|
-
|
|
30
23
|
check_required_apps(self.label)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Callable, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from dandy import Decoder
|
|
6
|
+
|
|
7
|
+
from django_spire.auth.controller.controller import AppAuthController
|
|
8
|
+
from django_spire.knowledge.intelligence.workflows.knowledge_workflow import knowledge_search_workflow
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from django.core.handlers.wsgi import WSGIRequest
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def generate_intent_decoder(
|
|
15
|
+
request: WSGIRequest,
|
|
16
|
+
default_callable: Callable | None = None
|
|
17
|
+
) -> Decoder:
|
|
18
|
+
intent_dict = {}
|
|
19
|
+
|
|
20
|
+
if AppAuthController(app_name='knowledge', request=request).can_view():
|
|
21
|
+
intent_dict['The user is looking for information or knowledge on something.'] = knowledge_search_workflow
|
|
22
|
+
|
|
23
|
+
if default_callable is not None:
|
|
24
|
+
intent_dict['None of the above choices match the user\'s intent'] = default_callable
|
|
25
|
+
|
|
26
|
+
return Decoder(
|
|
27
|
+
mapping_keys_description='Intent of the User\'s Request',
|
|
28
|
+
mapping=intent_dict
|
|
29
|
+
)
|
|
@@ -2,18 +2,24 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from dandy import Prompt
|
|
4
4
|
|
|
5
|
-
from django_spire.
|
|
5
|
+
from django_spire.ai.context.intelligence.prompts.organization_prompts import (
|
|
6
|
+
organization_info_prompt
|
|
7
|
+
)
|
|
6
8
|
|
|
7
9
|
|
|
8
|
-
def
|
|
10
|
+
def chat_system_prompt() -> Prompt:
|
|
9
11
|
return (
|
|
10
12
|
Prompt()
|
|
11
|
-
.text(
|
|
13
|
+
.text(
|
|
14
|
+
'You are a chat assistant, please use the organization information below for answering questions.'
|
|
15
|
+
)
|
|
12
16
|
.line_break()
|
|
13
|
-
.
|
|
17
|
+
.prompt(organization_info_prompt())
|
|
14
18
|
.line_break()
|
|
15
19
|
.text('Make sure to follow the rules below')
|
|
16
|
-
.list(
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
.list(
|
|
21
|
+
[
|
|
22
|
+
'Do not talk about being an AI at all to maintain the illusion of being a human assistant.',
|
|
23
|
+
]
|
|
24
|
+
)
|
|
19
25
|
)
|
|
@@ -1,71 +1,59 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
3
|
+
from typing import TYPE_CHECKING, Callable
|
|
4
4
|
|
|
5
|
-
from dandy import Prompt, Bot
|
|
6
5
|
from dandy.recorder import recorder_to_html_file
|
|
7
6
|
|
|
8
|
-
from django_spire.ai.chat.intelligence.
|
|
9
|
-
from django_spire.ai.
|
|
10
|
-
from django_spire.
|
|
11
|
-
from django_spire.
|
|
12
|
-
|
|
7
|
+
from django_spire.ai.chat.intelligence.decoders.tools import generate_intent_decoder
|
|
8
|
+
from django_spire.ai.decorators import log_ai_interaction_from_recorder
|
|
9
|
+
from django_spire.conf import settings
|
|
10
|
+
from django_spire.core.utils import (
|
|
11
|
+
get_callable_from_module_string_and_validate_arguments,
|
|
12
|
+
)
|
|
13
|
+
from django_spire.ai.chat.message_intel import BaseMessageIntel
|
|
13
14
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
15
16
|
from dandy.llm.request.message import MessageHistory
|
|
16
17
|
from django.core.handlers.wsgi import WSGIRequest
|
|
17
18
|
|
|
18
|
-
from django_spire.ai.chat.message_intel import BaseMessageIntel
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'The user is looking for information or knowledge on something.'
|
|
20
|
+
@recorder_to_html_file('spire_ai_chat_workflow')
|
|
21
|
+
def chat_workflow(
|
|
22
|
+
request: WSGIRequest,
|
|
23
|
+
user_input: str,
|
|
24
|
+
message_history: MessageHistory | None = None
|
|
25
|
+
) -> BaseMessageIntel:
|
|
26
|
+
if settings.AI_CHAT_DEFAULT_CALLABLE is not None:
|
|
27
|
+
default_process = get_callable_from_module_string_and_validate_arguments(
|
|
28
|
+
module_string=settings.AI_CHAT_DEFAULT_CALLABLE,
|
|
29
|
+
valid_args=('request', 'user_input', 'message_history'),
|
|
31
30
|
)
|
|
31
|
+
else:
|
|
32
|
+
default_process = None
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
intent_decoder = generate_intent_decoder(
|
|
35
|
+
request=request,
|
|
36
|
+
default_callable=default_process,
|
|
37
|
+
)
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
intent_process = intent_decoder.process(user_input, max_return_values=1)[0]
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return
|
|
41
|
+
@log_ai_interaction_from_recorder(request.user)
|
|
42
|
+
def run_workflow_process(callable_: Callable) -> BaseMessageIntel | None:
|
|
43
|
+
return callable_(
|
|
44
|
+
request=request,
|
|
45
|
+
user_input=user_input,
|
|
46
|
+
message_history=message_history,
|
|
47
|
+
)
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
@recorder_to_html_file('spire_chat_workflow')
|
|
43
|
-
def process(
|
|
44
|
-
request: WSGIRequest,
|
|
45
|
-
user_input: str,
|
|
46
|
-
message_history: MessageHistory | None = None
|
|
47
|
-
) -> BaseMessageIntel:
|
|
48
|
-
intent_decoder = SpireChatWorkflow._generate_intent_decoder(request)
|
|
49
|
-
intents = intent_decoder.process(user_input, max_return_values=1)
|
|
49
|
+
message_intel = run_workflow_process(intent_process)
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
if message_intel is None and default_process is not None:
|
|
52
|
+
message_intel = run_workflow_process(default_process)
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
postfix_system_prompt=organization_prompt()
|
|
59
|
-
)
|
|
54
|
+
if not issubclass(message_intel.__class__, BaseMessageIntel):
|
|
55
|
+
message = f'{intent_process.__qualname__} must return an instance of a {BaseMessageIntel.__name__} sub class.'
|
|
56
|
+
raise TypeError(message)
|
|
57
|
+
|
|
58
|
+
return message_intel
|
|
60
59
|
|
|
61
|
-
return chat_bot.llm.prompt_to_intel(
|
|
62
|
-
prompt=(
|
|
63
|
-
Prompt()
|
|
64
|
-
.text(f'User Input: {user_input}')
|
|
65
|
-
.line_break()
|
|
66
|
-
.text(intents[0].process(user_input))
|
|
67
|
-
),
|
|
68
|
-
intel_class=DefaultMessageIntel,
|
|
69
|
-
message_history=message_history,
|
|
70
|
-
postfix_system_prompt=organization_prompt()
|
|
71
|
-
)
|
django_spire/ai/chat/models.py
CHANGED
|
@@ -143,7 +143,8 @@ class ChatMessage(HistoryModelMixin):
|
|
|
143
143
|
return MessageResponse(
|
|
144
144
|
type=MessageResponseType(self.response_type),
|
|
145
145
|
sender=self.sender,
|
|
146
|
-
message_intel=self.intel
|
|
146
|
+
message_intel=self.intel,
|
|
147
|
+
message_timestamp=self.created_datetime.strftime('%b %d, %Y at %I:%M %p')
|
|
147
148
|
)
|
|
148
149
|
|
|
149
150
|
class Meta:
|
|
@@ -18,12 +18,14 @@ class MessageResponse:
|
|
|
18
18
|
type: MessageResponseType
|
|
19
19
|
sender: str
|
|
20
20
|
message_intel: BaseMessageIntel
|
|
21
|
+
message_timestamp: str | None = None
|
|
21
22
|
synthesis_speech: bool = False
|
|
22
23
|
|
|
23
24
|
def _render_template_to_html_string(self, template: str, context_data: dict[str, Any] | None = None) -> str:
|
|
24
25
|
return render_to_string(
|
|
25
26
|
template_name=template,
|
|
26
27
|
context={
|
|
28
|
+
'message_timestamp': self.message_timestamp,
|
|
27
29
|
'sender': self.sender,
|
|
28
30
|
'message_intel': self.message_intel,
|
|
29
31
|
'synthesis_speech': self.synthesis_speech,
|
|
@@ -63,6 +65,9 @@ class MessageResponseGroup:
|
|
|
63
65
|
def render_to_html_string(self, context_data: dict[str, Any] | None = None) -> str:
|
|
64
66
|
html_string = ''
|
|
65
67
|
|
|
68
|
+
context_data = context_data or {}
|
|
69
|
+
context_data['is_loading'] = context_data.get('is_loading', False)
|
|
70
|
+
|
|
66
71
|
for message_response in self.message_responses:
|
|
67
72
|
html_string += message_response.render_to_html_string(context_data)
|
|
68
73
|
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
{% block card_content %}
|
|
8
8
|
<div class="row">
|
|
9
|
-
<div class="col-12 vh-minus-nav">
|
|
10
|
-
{% include 'django_spire/ai/chat/widget/
|
|
9
|
+
<div class="col-12 vh-minus-top-nav">
|
|
10
|
+
{% include 'django_spire/ai/chat/widget/selection_widget.html' %}
|
|
11
11
|
</div>
|
|
12
12
|
</div>
|
|
13
13
|
{% endblock %}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
{% endblock %}
|
|
12
12
|
|
|
13
13
|
{% block dropdown_content %}
|
|
14
|
-
{% include 'django_spire/dropdown/element/dropdown_link_element.html' with x_click="enable_rename_chat()" link_text='Rename' link_css='text-start' %}
|
|
14
|
+
{% include 'django_spire/dropdown/element/dropdown_link_element.html' with x_click="enable_rename_chat(); toggle_dropdown()" link_text='Rename' link_css='text-start' %}
|
|
15
15
|
|
|
16
16
|
{% url 'django_spire:ai:chat:template:confirm_delete' pk=recent_chat.id as delete_url %}
|
|
17
17
|
{% include 'django_spire/dropdown/element/ellipsis_dropdown_modal_link_element.html' with view_url=delete_url link_text='Delete' link_css='text-app-danger text-start' %}
|
|
@@ -1,84 +1,122 @@
|
|
|
1
1
|
<div
|
|
2
|
-
class="
|
|
2
|
+
class="mb-1"
|
|
3
3
|
x-data="{
|
|
4
4
|
can_rename_chat: false,
|
|
5
5
|
chat_name: '{{ recent_chat.name|escapejs }}',
|
|
6
|
+
is_active: false,
|
|
6
7
|
new_chat_name: '{{ recent_chat.name|escapejs }}',
|
|
7
8
|
show_recent_chat: true,
|
|
8
9
|
|
|
10
|
+
cancel_rename_chat() {
|
|
11
|
+
this.new_chat_name = this.chat_name
|
|
12
|
+
this.can_rename_chat = false
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
confirm_rename_chat() {
|
|
16
|
+
this.rename_chat()
|
|
17
|
+
},
|
|
18
|
+
|
|
9
19
|
disable_rename_chat() {
|
|
10
20
|
this.can_rename_chat = false
|
|
11
21
|
},
|
|
22
|
+
|
|
12
23
|
enable_rename_chat() {
|
|
13
24
|
this.can_rename_chat = true
|
|
25
|
+
|
|
26
|
+
$nextTick(() => {
|
|
27
|
+
let input = this.$el.querySelector('input')
|
|
28
|
+
if (input) input.focus()
|
|
29
|
+
})
|
|
14
30
|
},
|
|
31
|
+
|
|
15
32
|
async rename_chat() {
|
|
33
|
+
if (this.new_chat_name.trim() === '') {
|
|
34
|
+
this.new_chat_name = this.chat_name
|
|
35
|
+
this.can_rename_chat = false
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
16
39
|
let response = await django_glue_fetch(
|
|
17
40
|
'{% url "django_spire:ai:chat:json:rename" pk=recent_chat.pk %}',
|
|
18
41
|
{payload: {new_name: this.new_chat_name}}
|
|
19
42
|
)
|
|
20
43
|
|
|
21
44
|
let type = response.type
|
|
45
|
+
|
|
22
46
|
if (type === 'success') {
|
|
23
47
|
this.chat_name = this.new_chat_name
|
|
24
48
|
} else {
|
|
25
49
|
$dispatch('notify', {'type': type, 'message': response.message})
|
|
50
|
+
this.new_chat_name = this.chat_name
|
|
26
51
|
}
|
|
27
52
|
|
|
28
53
|
this.can_rename_chat = false
|
|
29
54
|
}
|
|
30
55
|
}"
|
|
31
56
|
x-show="show_recent_chat"
|
|
57
|
+
@chat-activated.window="is_active = ($event.detail.chat_id === {{ recent_chat.id }})"
|
|
58
|
+
@click.outside="if (can_rename_chat) cancel_rename_chat()"
|
|
32
59
|
@deleted_chat.window="if (show_recent_chat) show_recent_chat = $event.detail !== {{ recent_chat.id }}"
|
|
33
60
|
>
|
|
34
|
-
<div
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
61
|
+
<div
|
|
62
|
+
:class="is_active ? 'bg-app-primary-soft' : ''"
|
|
63
|
+
class="d-flex align-items-center justify-content-between px-2 py-1 rounded"
|
|
64
|
+
data-chat-id="{{ recent_chat.id }}"
|
|
65
|
+
style="transition: background-color 0.15s ease;"
|
|
66
|
+
>
|
|
67
|
+
<div class="d-flex align-items-center flex-grow-1" style="min-width: 0; width: 0;">
|
|
68
|
+
<i
|
|
69
|
+
:class="{
|
|
70
|
+
'bi-chat-dots-fill': !can_rename_chat,
|
|
71
|
+
'bi-chat-dots': can_rename_chat,
|
|
72
|
+
'text-app-primary': is_active || can_rename_chat,
|
|
73
|
+
'text-muted': !is_active && !can_rename_chat
|
|
42
74
|
}"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
class="bi me-2"
|
|
76
|
+
style="flex-shrink: 0;"
|
|
77
|
+
></i>
|
|
78
|
+
|
|
79
|
+
<template x-if="!can_rename_chat">
|
|
80
|
+
<div @click="$dispatch('load-chat', {chat_id: {{ recent_chat.id }}})" class="cursor-pointer flex-grow-1 text-truncate">
|
|
81
|
+
<span class="px-2 fs-7" x-text="chat_name"></span>
|
|
82
|
+
</div>
|
|
83
|
+
</template>
|
|
84
|
+
|
|
85
|
+
<template x-if="can_rename_chat">
|
|
86
|
+
<div class="d-flex align-items-center position-relative w-100 py-0 px-1" style="flex: 1 1 0; min-width: 0;">
|
|
53
87
|
<input
|
|
54
|
-
x-model="new_chat_name"
|
|
55
|
-
class="col form-control d-inline p-0 mx-1 fs-7"
|
|
56
|
-
type="text"
|
|
57
|
-
style="max-width: 150px;"
|
|
58
|
-
@keydown.enter="rename_chat()"
|
|
59
|
-
@keydown.esc="disable_rename_chat()"
|
|
60
88
|
@focus="$el.select()"
|
|
89
|
+
@keydown.enter="confirm_rename_chat()"
|
|
90
|
+
@keydown.esc="cancel_rename_chat()"
|
|
91
|
+
class="form-control ps-1 pe-5 py-0 fs-7"
|
|
92
|
+
type="text"
|
|
93
|
+
x-init="$el.focus()"
|
|
94
|
+
x-model="new_chat_name"
|
|
61
95
|
/>
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
96
|
+
<div class="d-flex align-items-center position-absolute g-2" style="pointer-events: none; right: 8px;">
|
|
97
|
+
<i
|
|
98
|
+
@click="confirm_rename_chat()"
|
|
99
|
+
@mouseenter="$el.style.opacity = '0.75'"
|
|
100
|
+
@mouseleave="$el.style.opacity = '1'"
|
|
101
|
+
class="bi bi-check cursor-pointer text-success"
|
|
102
|
+
style="pointer-events: auto; transition: opacity 0.15s ease;"
|
|
103
|
+
title="Confirm"
|
|
104
|
+
></i>
|
|
105
|
+
<i
|
|
106
|
+
@click="cancel_rename_chat()"
|
|
107
|
+
@mouseenter="$el.style.opacity = '0.75'"
|
|
108
|
+
@mouseleave="$el.style.opacity = '1'"
|
|
109
|
+
class="bi bi-x cursor-pointer text-danger pe-1"
|
|
110
|
+
style="pointer-events: auto; transition: opacity 0.15s ease;"
|
|
111
|
+
title="Cancel"
|
|
112
|
+
></i>
|
|
113
|
+
</div>
|
|
80
114
|
</div>
|
|
81
|
-
|
|
115
|
+
</template>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div class="ms-2" style="flex-shrink: 0; visibility: hidden;" :style="can_rename_chat ? '' : 'visibility: visible;'">
|
|
119
|
+
{% include 'django_spire/ai/chat/dropdown/ellipsis_dropdown.html' %}
|
|
82
120
|
</div>
|
|
83
121
|
</div>
|
|
84
122
|
</div>
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{% load spire_core_tags %}
|
|
2
2
|
|
|
3
3
|
{% generate_id as response_message_intel_id %}
|
|
4
|
+
|
|
4
5
|
<div
|
|
5
6
|
x-data="{
|
|
6
7
|
show_loading: false,
|
|
7
8
|
show_reading: true,
|
|
8
9
|
show_typing: false,
|
|
10
|
+
|
|
9
11
|
init() {
|
|
10
12
|
this.response_message.render_outer($refs.loading_response_message)
|
|
11
13
|
|
|
@@ -14,6 +16,7 @@
|
|
|
14
16
|
setTimeout(() => {this.show_reading = false}, 4000)
|
|
15
17
|
setTimeout(() => {this.show_typing = true}, 4000)
|
|
16
18
|
},
|
|
19
|
+
|
|
17
20
|
response_message: new ViewGlue(
|
|
18
21
|
'{% url "django_spire:ai:chat:message:response:new" %}',
|
|
19
22
|
{
|
|
@@ -27,18 +30,20 @@
|
|
|
27
30
|
x-cloak
|
|
28
31
|
>
|
|
29
32
|
{{ message_intel.text|json_script:response_message_intel_id }}
|
|
33
|
+
|
|
30
34
|
<div x-ref="loading_response_message" class="row mt-2 pb-3">
|
|
31
35
|
<div class="col-auto align-self-center">
|
|
32
36
|
<div class="row align-items-center">
|
|
33
37
|
<div class="col-12 fs--1">
|
|
34
38
|
{{ chat_workflow_name }}
|
|
35
39
|
</div>
|
|
40
|
+
|
|
36
41
|
<div class="col-12 ps-4 pt-2">
|
|
37
|
-
<span class="spinner-border spinner-border-sm" role="status">
|
|
42
|
+
<span class="spinner-border spinner-border-sm text-muted" role="status">
|
|
38
43
|
<span class="visually-hidden">Loading...</span>
|
|
39
44
|
</span>
|
|
40
|
-
<span x-show="show_reading">Reading...</span>
|
|
41
|
-
<span x-show="show_typing">Typing...</span>
|
|
45
|
+
<span class="text-muted" x-show="show_reading">Reading...</span>
|
|
46
|
+
<span class="text-muted" x-show="show_typing">Typing...</span>
|
|
42
47
|
</div>
|
|
43
48
|
</div>
|
|
44
49
|
</div>
|
|
@@ -2,29 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
{% block message %}
|
|
4
4
|
{% generate_id as message_intel_id %}
|
|
5
|
+
|
|
5
6
|
<div
|
|
6
7
|
class="col-auto mb-1 {{ message_class }}"
|
|
7
8
|
@mouseover="show_message_menu = true"
|
|
8
9
|
@mouseleave="show_message_menu = false"
|
|
9
10
|
x-data="{
|
|
10
|
-
show_message_menu: false,
|
|
11
11
|
message_intel_str: '',
|
|
12
|
+
show_message_menu: false,
|
|
13
|
+
|
|
12
14
|
init() {
|
|
13
15
|
this.message_intel_str = JSON.parse(document.getElementById('{{ message_intel_id }}').textContent)
|
|
14
|
-
{% if synthesis_speech %}
|
|
15
|
-
$el.scrollIntoView({behavior: 'smooth'})
|
|
16
|
+
{% if synthesis_speech %}$dispatch('speak', {text: this.message_intel_str}){% endif %}
|
|
17
|
+
{% if not is_loading %}$el.scrollIntoView({behavior: 'smooth'}){% endif %}
|
|
16
18
|
}
|
|
17
19
|
}"
|
|
18
20
|
>
|
|
19
21
|
<div class="container-fluid">
|
|
20
22
|
{{ message_intel.content_to_str|json_script:message_intel_id }}
|
|
23
|
+
|
|
21
24
|
<div class="row">
|
|
22
|
-
<div class="col
|
|
25
|
+
<div class="col px-2 text-muted {{ sender_class }}" style="font-size: 0.7rem;">
|
|
23
26
|
{% block message_sender %}
|
|
24
|
-
{{ sender }}
|
|
27
|
+
<span class="fw-semibold">{{ sender }}</span>
|
|
25
28
|
{% endblock %}
|
|
29
|
+
|
|
30
|
+
{% if message_timestamp %}
|
|
31
|
+
<span>- {{ message_timestamp }}</span>
|
|
32
|
+
{% endif %}
|
|
26
33
|
</div>
|
|
27
34
|
</div>
|
|
35
|
+
|
|
28
36
|
<div class="row">
|
|
29
37
|
<div class="col-12 px-3 py-2 rounded-3 shadow-sm {{ content_class|default:'border' }}">
|
|
30
38
|
{% block message_content %}
|
|
@@ -32,17 +40,18 @@
|
|
|
32
40
|
{% endblock %}
|
|
33
41
|
</div>
|
|
34
42
|
</div>
|
|
43
|
+
|
|
35
44
|
<div
|
|
36
45
|
class="row justify-content-end"
|
|
37
46
|
:class="show_message_menu ? 'opacity-100' : 'opacity-0'"
|
|
38
47
|
>
|
|
39
48
|
<div class="col-auto px-0">
|
|
40
|
-
<div @click="
|
|
49
|
+
<div @click="$dispatch('speak', {text: message_intel_str})" class="py-1 px-2 cursor-pointer">
|
|
41
50
|
<i class="bi bi-soundwave"></i>
|
|
42
51
|
</div>
|
|
43
52
|
</div>
|
|
44
53
|
<div class="col-auto pe-0 ps-1">
|
|
45
|
-
<div @click="
|
|
54
|
+
<div @click="$dispatch('send-message', {message_body: message_intel_str})" class="py-1 px-2 cursor-pointer">
|
|
46
55
|
<i class="bi bi-repeat"></i>
|
|
47
56
|
</div>
|
|
48
57
|
</div>
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
<div
|
|
2
|
-
class="row justify-content-end ps-4 ps-lg-5"
|
|
3
|
-
x-data="{
|
|
1
|
+
<div class="row justify-content-end ps-4 ps-lg-5" data-message-chat-id="{{ chat_id }}" {% if not is_loading %}x-data="{
|
|
4
2
|
show_message: false,
|
|
3
|
+
|
|
5
4
|
init() {
|
|
6
5
|
setTimeout(() => this.show_message = true, 500)
|
|
7
6
|
setTimeout(() => $el.scrollIntoView({behavior: 'smooth'}), 550)
|
|
8
7
|
}
|
|
9
|
-
}"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
>
|
|
13
|
-
{% include message_intel.template with sender_class="text-end" speech_synthesis_class='justify-content-end' content_class="bg-app-layer-three" %}
|
|
8
|
+
}" x-show="show_message" x-transition{% endif %}>
|
|
9
|
+
|
|
10
|
+
{% include message_intel.template with sender_class="text-end" speech_synthesis_class='justify-content-end' content_class="bg-app-primary-soft" %}
|
|
14
11
|
</div>
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
<div
|
|
2
|
-
x-data="{
|
|
3
|
-
show_message: false,
|
|
4
|
-
init() {
|
|
5
|
-
setTimeout(() => this.show_message = true, 300)
|
|
6
|
-
setTimeout(() => $el.scrollIntoView({behavior: 'smooth'}), 350)
|
|
7
|
-
}
|
|
8
|
-
}"
|
|
9
2
|
class="row justify-content-start pe-4 pe-lg-5"
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
data-message-chat-id="{{ chat_id }}"
|
|
4
|
+
{% if not is_loading %}
|
|
5
|
+
x-data="{
|
|
6
|
+
show_message: false,
|
|
7
|
+
|
|
8
|
+
init() {
|
|
9
|
+
setTimeout(() => this.show_message = true, 300)
|
|
10
|
+
setTimeout(() => $el.scrollIntoView({behavior: 'smooth'}), 350)
|
|
11
|
+
}
|
|
12
|
+
}"
|
|
13
|
+
x-show="show_message"
|
|
14
|
+
x-transition
|
|
15
|
+
{% endif %}
|
|
12
16
|
>
|
|
13
|
-
{% include message_intel.template with content_class="bg-app-
|
|
17
|
+
{% include message_intel.template with content_class="bg-app-warning-soft" %}
|
|
14
18
|
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{% extends 'django_spire/page/full_page.html' %}
|
|
2
|
+
|
|
3
|
+
{% block full_page_sub_navigation_title %}
|
|
4
|
+
Chat History
|
|
5
|
+
{% endblock %}
|
|
6
|
+
|
|
7
|
+
{% block full_page_sub_navigation %}
|
|
8
|
+
{% include 'django_spire/ai/chat/widget/selection_widget.html' %}
|
|
9
|
+
{% endblock %}
|
|
10
|
+
|
|
11
|
+
{% block full_page_content %}
|
|
12
|
+
<div
|
|
13
|
+
class="row"
|
|
14
|
+
x-data="{
|
|
15
|
+
chat_load_ajax_view: new ViewGlue('{% url "django_spire:ai:chat:template:load" %}'),
|
|
16
|
+
|
|
17
|
+
init() {
|
|
18
|
+
this.load_chat(0)
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
async load_chat(chat_id) {
|
|
22
|
+
await this.chat_load_ajax_view.render_inner(this.$refs.chat_dialog, {
|
|
23
|
+
chat_id: chat_id
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
$dispatch('chat-loaded')
|
|
28
|
+
}
|
|
29
|
+
}"
|
|
30
|
+
@deleted_chat.window="load_chat(0)"
|
|
31
|
+
@load-chat.window="load_chat($event.detail.chat_id)"
|
|
32
|
+
>
|
|
33
|
+
<div class="col" x-ref="chat_dialog"></div>
|
|
34
|
+
</div>
|
|
35
|
+
{% endblock %}
|