django-spire 0.18.3__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.
Files changed (234) hide show
  1. django_spire/ai/admin.py +1 -0
  2. django_spire/ai/chat/apps.py +1 -8
  3. django_spire/ai/chat/intelligence/decoders/tools.py +29 -0
  4. django_spire/ai/chat/intelligence/prompts.py +13 -7
  5. django_spire/ai/chat/intelligence/workflows/chat_workflow.py +40 -52
  6. django_spire/ai/chat/models.py +2 -1
  7. django_spire/ai/chat/querysets.py +1 -3
  8. django_spire/ai/chat/responses.py +5 -0
  9. django_spire/ai/chat/templates/django_spire/ai/chat/card/chat_card.html +2 -2
  10. django_spire/ai/chat/templates/django_spire/ai/chat/dropdown/ellipsis_dropdown.html +1 -1
  11. django_spire/ai/chat/templates/django_spire/ai/chat/element/recent_chat_select_element.html +82 -44
  12. django_spire/ai/chat/templates/django_spire/ai/chat/message/loading_response_message.html +8 -3
  13. django_spire/ai/chat/templates/django_spire/ai/chat/message/message.html +16 -7
  14. django_spire/ai/chat/templates/django_spire/ai/chat/message/request_message.html +5 -8
  15. django_spire/ai/chat/templates/django_spire/ai/chat/message/response_message.html +14 -10
  16. django_spire/ai/chat/templates/django_spire/ai/chat/page/chat_page.html +35 -0
  17. django_spire/ai/chat/templates/django_spire/ai/chat/widget/dialog_widget.html +72 -23
  18. django_spire/ai/chat/templates/django_spire/ai/chat/widget/selection_widget.html +42 -0
  19. django_spire/ai/chat/tests/test_urls/test_json_urls.py +4 -2
  20. django_spire/ai/chat/urls/__init__.py +1 -1
  21. django_spire/ai/chat/urls/json_urls.py +1 -1
  22. django_spire/ai/chat/urls/page_urls.py +1 -1
  23. django_spire/ai/chat/urls/{template_urls.py → template/template_urls.py} +1 -1
  24. django_spire/ai/chat/views/json_views.py +4 -3
  25. django_spire/ai/chat/views/message_request_views.py +32 -14
  26. django_spire/ai/chat/views/message_response_views.py +14 -11
  27. django_spire/ai/chat/views/message_views.py +7 -1
  28. django_spire/ai/chat/views/page_views.py +2 -2
  29. django_spire/ai/chat/views/{template_views.py → template/template_views.py} +1 -7
  30. django_spire/ai/context/__init__.py +0 -0
  31. django_spire/ai/context/admin.py +15 -0
  32. django_spire/ai/context/apps.py +16 -0
  33. django_spire/ai/context/choices.py +11 -0
  34. django_spire/ai/context/intelligence/__init__.py +0 -0
  35. django_spire/ai/context/intelligence/prompts/__init__.py +0 -0
  36. django_spire/ai/context/intelligence/prompts/organization_prompts.py +19 -0
  37. django_spire/ai/context/migrations/0001_initial.py +67 -0
  38. django_spire/ai/context/migrations/__init__.py +0 -0
  39. django_spire/ai/context/models.py +67 -0
  40. django_spire/ai/context/querysets.py +15 -0
  41. django_spire/ai/context/seeding/__init__.py +0 -0
  42. django_spire/ai/context/seeding/seed.py +24 -0
  43. django_spire/ai/prompt/system/bots.py +3 -5
  44. django_spire/ai/prompt/tuning/bots.py +5 -10
  45. django_spire/ai/sms/admin.py +2 -0
  46. django_spire/ai/sms/apps.py +2 -7
  47. django_spire/ai/sms/decorators.py +2 -2
  48. django_spire/ai/sms/intelligence/workflows/sms_conversation_workflow.py +18 -18
  49. django_spire/ai/sms/tests/test_webhook.py +5 -4
  50. django_spire/ai/sms/urls.py +3 -0
  51. django_spire/ai/sms/views.py +3 -5
  52. django_spire/ai/urls.py +2 -0
  53. django_spire/auth/templates/django_spire/auth/form/login_form.html +25 -0
  54. django_spire/auth/templates/django_spire/auth/page/auth_page.html +2 -3
  55. django_spire/auth/templates/django_spire/auth/page/login_page.html +1 -25
  56. django_spire/conf.py +1 -3
  57. django_spire/consts.py +1 -7
  58. django_spire/contrib/seeding/intelligence/bots/seeder_generator_bot.py +3 -2
  59. django_spire/contrib/utils.py +2 -0
  60. django_spire/core/static/django_spire/css/app-button.css +11 -1
  61. django_spire/core/static/django_spire/css/app-import.css +2 -0
  62. django_spire/core/static/django_spire/css/app-layout.css +15 -0
  63. django_spire/core/static/django_spire/css/app-navigation.css +24 -2
  64. django_spire/core/static/django_spire/css/app-page.css +8 -0
  65. django_spire/core/static/django_spire/css/app-side-panel.css +102 -0
  66. django_spire/core/static/django_spire/css/app-text.css +19 -1
  67. django_spire/core/static/django_spire/css/bootstrap-extension.css +128 -7
  68. django_spire/core/static/django_spire/css/bootstrap-override.css +161 -111
  69. django_spire/core/static/django_spire/css/themes/ayu/app-dark.css +0 -4
  70. django_spire/core/static/django_spire/css/themes/ayu/app-light.css +0 -4
  71. django_spire/core/static/django_spire/css/themes/catppuccin/app-dark.css +0 -4
  72. django_spire/core/static/django_spire/css/themes/catppuccin/app-light.css +0 -4
  73. django_spire/core/static/django_spire/css/themes/default/app-dark.css +0 -4
  74. django_spire/core/static/django_spire/css/themes/default/app-light.css +0 -4
  75. django_spire/core/static/django_spire/css/themes/dracula/app-dark.css +0 -4
  76. django_spire/core/static/django_spire/css/themes/dracula/app-light.css +0 -4
  77. django_spire/core/static/django_spire/css/themes/gruvbox/app-dark.css +0 -4
  78. django_spire/core/static/django_spire/css/themes/gruvbox/app-light.css +0 -4
  79. django_spire/core/static/django_spire/css/themes/material/app-dark.css +0 -4
  80. django_spire/core/static/django_spire/css/themes/material/app-light.css +0 -4
  81. django_spire/core/static/django_spire/css/themes/nord/app-dark.css +0 -4
  82. django_spire/core/static/django_spire/css/themes/nord/app-light.css +0 -4
  83. django_spire/core/static/django_spire/css/themes/oceanic-next/app-dark.css +0 -4
  84. django_spire/core/static/django_spire/css/themes/oceanic-next/app-light.css +0 -4
  85. django_spire/core/static/django_spire/css/themes/one-dark/app-dark.css +0 -4
  86. django_spire/core/static/django_spire/css/themes/one-dark/app-light.css +0 -4
  87. django_spire/core/static/django_spire/css/themes/palenight/app-dark.css +0 -4
  88. django_spire/core/static/django_spire/css/themes/palenight/app-light.css +0 -4
  89. django_spire/core/static/django_spire/css/themes/rose-pine/app-dark.css +0 -4
  90. django_spire/core/static/django_spire/css/themes/rose-pine/app-light.css +0 -4
  91. django_spire/core/static/django_spire/css/themes/synthwave/app-dark.css +0 -4
  92. django_spire/core/static/django_spire/css/themes/synthwave/app-light.css +0 -4
  93. django_spire/core/static/django_spire/css/themes/tokyo-night/app-dark.css +0 -4
  94. django_spire/core/static/django_spire/css/themes/tokyo-night/app-light.css +0 -4
  95. django_spire/core/static/django_spire/font/Poppins-Black.ttf +0 -0
  96. django_spire/core/static/django_spire/font/Poppins-BlackItalic.ttf +0 -0
  97. django_spire/core/static/django_spire/font/Poppins-Bold.ttf +0 -0
  98. django_spire/core/static/django_spire/font/Poppins-BoldItalic.ttf +0 -0
  99. django_spire/core/static/django_spire/font/Poppins-ExtraBold.ttf +0 -0
  100. django_spire/core/static/django_spire/font/Poppins-ExtraBoldItalic.ttf +0 -0
  101. django_spire/core/static/django_spire/font/Poppins-ExtraLight.ttf +0 -0
  102. django_spire/core/static/django_spire/font/Poppins-ExtraLightItalic.ttf +0 -0
  103. django_spire/core/static/django_spire/font/Poppins-Italic.ttf +0 -0
  104. django_spire/core/static/django_spire/font/Poppins-Light.ttf +0 -0
  105. django_spire/core/static/django_spire/font/Poppins-LightItalic.ttf +0 -0
  106. django_spire/core/static/django_spire/font/Poppins-Medium.ttf +0 -0
  107. django_spire/core/static/django_spire/font/Poppins-MediumItalic.ttf +0 -0
  108. django_spire/core/static/django_spire/font/Poppins-SemiBold.ttf +0 -0
  109. django_spire/core/static/django_spire/font/Poppins-SemiBoldItalic.ttf +0 -0
  110. django_spire/core/static/django_spire/font/Poppins-Thin.ttf +0 -0
  111. django_spire/core/static/django_spire/font/Poppins-ThinItalic.ttf +0 -0
  112. django_spire/core/static/django_spire/js/ui.js +12 -0
  113. django_spire/core/templates/django_spire/button/base_button.html +1 -1
  114. django_spire/core/templates/django_spire/button/primary_dark_outlined_button.html +3 -0
  115. django_spire/core/templates/django_spire/dropdown/element/dropdown_link_element.html +12 -8
  116. django_spire/core/templates/django_spire/dropdown/element/ellipsis_dropdown_modal_link_element.html +2 -1
  117. django_spire/core/templates/django_spire/dropdown/ellipsis_dropdown.html +2 -1
  118. django_spire/core/templates/django_spire/navigation/accordion/nav_accordion.html +1 -1
  119. django_spire/core/templates/django_spire/navigation/elements/nav_link.html +1 -1
  120. django_spire/core/templates/django_spire/navigation/elements/nav_title_divider.html +1 -1
  121. django_spire/core/templates/django_spire/navigation/side_navigation.html +52 -8
  122. django_spire/core/templates/django_spire/navigation/top_navigation.html +8 -2
  123. django_spire/core/templates/django_spire/page/full_page.html +218 -11
  124. django_spire/core/utils.py +26 -2
  125. django_spire/knowledge/collection/models.py +24 -0
  126. django_spire/knowledge/collection/services/transformation_service.py +2 -1
  127. django_spire/knowledge/collection/tests/test_services/test_transformation_service.py +44 -42
  128. django_spire/knowledge/collection/urls/form_urls.py +1 -0
  129. django_spire/knowledge/collection/urls/page_urls.py +1 -0
  130. django_spire/knowledge/collection/views/form_views.py +13 -2
  131. django_spire/knowledge/collection/views/page_views.py +36 -4
  132. django_spire/knowledge/entry/models.py +32 -0
  133. django_spire/knowledge/entry/services/transformation_services.py +10 -10
  134. django_spire/knowledge/entry/version/block/data/data.py +2 -1
  135. django_spire/knowledge/entry/version/block/data/heading_data.py +2 -2
  136. django_spire/knowledge/entry/version/block/data/list/data.py +3 -4
  137. django_spire/knowledge/entry/version/block/data/maps.py +3 -5
  138. django_spire/knowledge/entry/version/block/data/text_data.py +2 -2
  139. django_spire/knowledge/entry/version/block/models.py +10 -7
  140. django_spire/knowledge/entry/version/block/services/factory_service.py +11 -10
  141. django_spire/knowledge/entry/version/block/tests/factories.py +6 -5
  142. django_spire/knowledge/entry/version/converters/docx_converter.py +1 -1
  143. django_spire/knowledge/entry/version/intelligence/bots/markdown_format_llm_bot.py +14 -12
  144. django_spire/knowledge/entry/version/seeding/seeder.py +3 -3
  145. django_spire/knowledge/entry/version/services/processor_service.py +36 -10
  146. django_spire/knowledge/entry/version/tests/test_converters/test_docx_converter.py +1 -1
  147. django_spire/knowledge/entry/version/tests/test_urls/test_page_urls.py +1 -1
  148. django_spire/knowledge/entry/version/urls/page_urls.py +1 -1
  149. django_spire/knowledge/entry/version/views/json_views.py +5 -3
  150. django_spire/knowledge/entry/version/views/page_views.py +11 -12
  151. django_spire/knowledge/entry/version/views/redirect_views.py +1 -1
  152. django_spire/knowledge/entry/views/form_views.py +1 -1
  153. django_spire/knowledge/intelligence/bots/entry_search_llm_bot.py +33 -11
  154. django_spire/knowledge/intelligence/decoders/__init__.py +0 -0
  155. django_spire/knowledge/intelligence/{maps/collection_map.py → decoders/collection_decoder.py} +5 -5
  156. django_spire/knowledge/intelligence/{maps/entry_map.py → decoders/entry_decoder.py} +3 -3
  157. django_spire/knowledge/intelligence/intel/collection_intel.py +1 -0
  158. django_spire/knowledge/intelligence/intel/entry_intel.py +5 -2
  159. django_spire/knowledge/intelligence/intel/message_intel.py +2 -0
  160. django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +43 -51
  161. django_spire/knowledge/migrations/0007_alter_collection_options.py +17 -0
  162. django_spire/knowledge/static/django_spire/knowledge/entry/version/js/editor.js +24 -5
  163. django_spire/knowledge/templates/django_spire/knowledge/collection/card/top_level_list_card.html +21 -0
  164. django_spire/knowledge/templates/django_spire/knowledge/collection/component/x_collection_navigation.html +15 -6
  165. django_spire/knowledge/templates/django_spire/knowledge/collection/element/ellipsis_dropdown.html +22 -0
  166. django_spire/knowledge/templates/django_spire/knowledge/collection/form/form.html +21 -9
  167. django_spire/knowledge/templates/django_spire/knowledge/collection/item/collection_item.html +19 -0
  168. django_spire/knowledge/templates/django_spire/knowledge/collection/page/display_page.html +49 -0
  169. django_spire/knowledge/templates/django_spire/knowledge/collection/page/form_page.html +1 -1
  170. django_spire/knowledge/templates/django_spire/knowledge/container/container.html +33 -0
  171. django_spire/knowledge/templates/django_spire/knowledge/entry/file/page/list_page.html +1 -1
  172. django_spire/knowledge/templates/django_spire/knowledge/entry/item/list_item.html +2 -2
  173. django_spire/knowledge/templates/django_spire/knowledge/entry/page/form_page.html +1 -1
  174. django_spire/knowledge/templates/django_spire/knowledge/entry/page/import_form_page.html +1 -1
  175. django_spire/knowledge/templates/django_spire/knowledge/entry/version/container/detail_container.html +51 -45
  176. django_spire/knowledge/templates/django_spire/knowledge/entry/version/page/editor_page.html +54 -0
  177. django_spire/knowledge/templates/django_spire/knowledge/entry/version/page/form_page.html +1 -1
  178. django_spire/knowledge/templates/django_spire/knowledge/message/knowledge_message_intel.html +18 -1
  179. django_spire/knowledge/templates/django_spire/knowledge/page/full_page.html +37 -0
  180. django_spire/knowledge/templates/django_spire/knowledge/page/home_page.html +2 -2
  181. django_spire/knowledge/templates/django_spire/knowledge/sub_navigation/item/collection_sub_navigation_item.html +29 -0
  182. django_spire/knowledge/templates/django_spire/knowledge/sub_navigation/item/entry_sub_navigation_item.html +20 -0
  183. django_spire/knowledge/templates/django_spire/knowledge/{navigation/content/navigation_content.html → sub_navigation/widget/collection_entry_sub_navigation_widget.html} +42 -19
  184. django_spire/knowledge/views/page_views.py +4 -4
  185. django_spire/settings.py +3 -3
  186. django_spire/theme/templates/django_spire/theme/card/badges_preview_card.html +0 -1
  187. django_spire/theme/templates/django_spire/theme/card/base_preview_card.html +1 -0
  188. django_spire/theme/templates/django_spire/theme/card/typography_preview_card.html +1 -1
  189. django_spire/theme/templates/django_spire/theme/example/form/example_form.html +2 -0
  190. django_spire/theme/templates/django_spire/theme/example/form/example_form_card.html +5 -0
  191. django_spire/theme/templates/django_spire/theme/page/badges_page.html +7 -9
  192. django_spire/theme/templates/django_spire/theme/page/borders_page.html +6 -9
  193. django_spire/theme/templates/django_spire/theme/page/buttons_page.html +6 -9
  194. django_spire/theme/templates/django_spire/theme/page/colors_page.html +6 -6
  195. django_spire/theme/templates/django_spire/theme/page/dashboard_page.html +57 -0
  196. django_spire/theme/templates/django_spire/theme/page/django_glue_page.html +7 -9
  197. django_spire/theme/templates/django_spire/theme/page/theme_page.html +24 -0
  198. django_spire/theme/templates/django_spire/theme/page/typography_page.html +6 -9
  199. django_spire/theme/templates/django_spire/theme/section/badge_section_card.html +5 -0
  200. django_spire/theme/templates/django_spire/theme/section/border_section_card.html +5 -0
  201. django_spire/theme/templates/django_spire/theme/section/button_section.html +1 -0
  202. django_spire/theme/templates/django_spire/theme/section/button_section_card.html +5 -0
  203. django_spire/theme/templates/django_spire/theme/section/color_section.html +10 -10
  204. django_spire/theme/templates/django_spire/theme/section/color_section_card.html +5 -0
  205. django_spire/theme/templates/django_spire/theme/section/typography_section.html +41 -1
  206. django_spire/theme/templates/django_spire/theme/section/typography_section_card.html +5 -0
  207. {django_spire-0.18.3.dist-info → django_spire-0.19.0.dist-info}/METADATA +3 -3
  208. {django_spire-0.18.3.dist-info → django_spire-0.19.0.dist-info}/RECORD +217 -178
  209. django_spire/ai/chat/intelligence/bots/chat_bot.py +0 -8
  210. django_spire/ai/chat/intelligence/maps/intent_llm_map.py +0 -10
  211. django_spire/ai/chat/templates/django_spire/ai/chat/page/home_page.html +0 -9
  212. django_spire/ai/chat/templates/django_spire/ai/chat/widget/chat_widget.html +0 -64
  213. django_spire/ai/chat/templates/django_spire/ai/chat/widget/search_chat_results_widget.html +0 -25
  214. django_spire/ai/chat/templates/django_spire/ai/chat/widget/search_chat_widget.html +0 -39
  215. django_spire/ai/chat/templates/django_spire/ai/chat/widget/select_chat_widget.html +0 -24
  216. django_spire/ai/chat/tools.py +0 -57
  217. django_spire/ai/sms/tools.py +0 -57
  218. django_spire/core/static/django_spire/font/Karla-Bold.ttf +0 -0
  219. django_spire/core/static/django_spire/font/Merriweather.ttf +0 -0
  220. django_spire/knowledge/context_processors.py +0 -18
  221. django_spire/knowledge/templates/django_spire/knowledge/entry/version/page/detail_page.html +0 -25
  222. django_spire/knowledge/templates/django_spire/knowledge/navigation/card/navigation_card.html +0 -9
  223. django_spire/knowledge/templates/django_spire/knowledge/navigation/item/collection/collection_item.html +0 -29
  224. django_spire/knowledge/templates/django_spire/knowledge/navigation/item/entry/entry_item.html +0 -18
  225. django_spire/knowledge/templates/django_spire/knowledge/navigation/page/full_page.html +0 -90
  226. /django_spire/ai/chat/intelligence/{bots → decoders}/__init__.py +0 -0
  227. /django_spire/ai/chat/{intelligence/maps → urls/template}/__init__.py +0 -0
  228. /django_spire/{knowledge/intelligence/maps → ai/chat/views/template}/__init__.py +0 -0
  229. /django_spire/knowledge/entry/version/{constants.py → consts.py} +0 -0
  230. /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
  231. /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
  232. {django_spire-0.18.3.dist-info → django_spire-0.19.0.dist-info}/WHEEL +0 -0
  233. {django_spire-0.18.3.dist-info → django_spire-0.19.0.dist-info}/licenses/LICENSE.md +0 -0
  234. {django_spire-0.18.3.dist-info → django_spire-0.19.0.dist-info}/top_level.txt +0 -0
@@ -42,15 +42,15 @@ class EntryTransformationService(BaseDjangoModelService['Entry']):
42
42
  if current_version.published_datetime else ''
43
43
  ),
44
44
  'status': current_version.status,
45
- 'delete_url': f'''
45
+ 'delete_url': f"""
46
46
  {site}{
47
47
  reverse(
48
48
  'django_spire:knowledge:entry:page:delete',
49
49
  kwargs={'pk': self.obj.pk},
50
50
  )
51
51
  }
52
- ''',
53
- 'edit_url': f'''
52
+ """,
53
+ 'edit_url': f"""
54
54
  {site}{
55
55
  reverse(
56
56
  'django_spire:knowledge:entry:form:update',
@@ -60,21 +60,21 @@ class EntryTransformationService(BaseDjangoModelService['Entry']):
60
60
  },
61
61
  )
62
62
  }
63
- ''',
64
- 'view_url': f'''
63
+ """,
64
+ 'view_url': f"""
65
65
  {site}{
66
66
  reverse(
67
- 'django_spire:knowledge:entry:version:page:detail',
67
+ 'django_spire:knowledge:entry:version:page:editor',
68
68
  kwargs={'pk': current_version.pk},
69
69
  )
70
70
  }
71
- ''',
72
- 'edit_version_url': f'''
71
+ """,
72
+ 'edit_version_url': f"""
73
73
  {site}{
74
74
  reverse(
75
- 'django_spire:knowledge:entry:version:page:detail',
75
+ 'django_spire:knowledge:entry:version:page:editor',
76
76
  kwargs={'pk': current_version.pk},
77
77
  )
78
78
  }?view_mode=edit
79
- '''
79
+ """
80
80
  }
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from pydantic import BaseModel
4
4
 
5
5
 
6
- class BaseEditorBlockData(BaseModel):
6
+ class BaseEditorJsBlockData(BaseModel):
7
7
  """
8
8
  This class serves as a foundational abstract model for EditorJS tool data objects.
9
9
 
@@ -29,5 +29,6 @@ class BaseEditorBlockData(BaseModel):
29
29
 
30
30
  Raises:
31
31
  NotImplementedError: If the method is not implemented in a subclass.
32
+
32
33
  """
33
34
  raise NotImplementedError
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
- from django_spire.knowledge.entry.version.block.data.data import BaseEditorBlockData
3
+ from django_spire.knowledge.entry.version.block.data.data import BaseEditorJsBlockData
4
4
 
5
5
 
6
- class HeadingEditorBlockData(BaseEditorBlockData):
6
+ class HeadingEditorBlockData(BaseEditorJsBlockData):
7
7
  text: str
8
8
  level: int
9
9
 
@@ -1,18 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Optional
4
3
 
5
4
  from pydantic import model_validator, BaseModel
6
5
 
7
6
  from django_spire.knowledge.entry.version.block.constants import SPACES_PER_INDENT
8
- from django_spire.knowledge.entry.version.block.data.data import BaseEditorBlockData
7
+ from django_spire.knowledge.entry.version.block.data.data import BaseEditorJsBlockData
9
8
  from django_spire.knowledge.entry.version.block.data.list.meta import ChecklistItemMeta, \
10
9
  OrderedListItemMeta
11
10
  from django_spire.knowledge.entry.version.block.data.list.choices import \
12
11
  ListEditorBlockDataStyle
13
12
 
14
13
 
15
- class ListEditorBlockData(BaseEditorBlockData):
14
+ class ListEditorBlockData(BaseEditorJsBlockData):
16
15
  style: ListEditorBlockDataStyle | str
17
16
  meta: ChecklistItemMeta | OrderedListItemMeta | None = None
18
17
  items: list[ListItemEditorBlockData]
@@ -41,7 +40,7 @@ class ListEditorBlockData(BaseEditorBlockData):
41
40
  class ListItemEditorBlockData(BaseModel):
42
41
  content: str
43
42
  meta: ChecklistItemMeta | OrderedListItemMeta | dict | None = None
44
- items: Optional[list[ListItemEditorBlockData]] = []
43
+ items: list[ListItemEditorBlockData] | None = []
45
44
 
46
45
  def get_prefix(
47
46
  self,
@@ -9,15 +9,13 @@ from django_spire.knowledge.entry.version.block.data.text_data import \
9
9
  TextEditorBlockData
10
10
 
11
11
 
12
- EDITOR_BLOCK_DATA_MAP = {
12
+ EDITOR_JS_BLOCK_DATA_MAP = {
13
13
  BlockTypeChoices.TEXT: TextEditorBlockData,
14
14
  BlockTypeChoices.HEADING: HeadingEditorBlockData,
15
15
  BlockTypeChoices.LIST: ListEditorBlockData,
16
16
  }
17
17
 
18
18
 
19
- EDITOR_BLOCK_DATA_REVERSE_MAP = {
20
- TextEditorBlockData: BlockTypeChoices.TEXT,
21
- HeadingEditorBlockData: BlockTypeChoices.HEADING,
22
- ListEditorBlockData: BlockTypeChoices.LIST
19
+ EDITOR_JS_BLOCK_DATA_REVERSE_MAP = {
20
+ value: key for key, value in EDITOR_JS_BLOCK_DATA_MAP.items()
23
21
  }
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
- from django_spire.knowledge.entry.version.block.data.data import BaseEditorBlockData
3
+ from django_spire.knowledge.entry.version.block.data.data import BaseEditorJsBlockData
4
4
 
5
5
 
6
- class TextEditorBlockData(BaseEditorBlockData):
6
+ class TextEditorBlockData(BaseEditorJsBlockData):
7
7
  text: str
8
8
 
9
9
  def render_to_text(self) -> str:
@@ -5,8 +5,8 @@ from django.db import models
5
5
  from django_spire.contrib.ordering.mixins import OrderingModelMixin
6
6
  from django_spire.history.mixins import HistoryModelMixin
7
7
  from django_spire.knowledge.entry.version.block.choices import BlockTypeChoices
8
- from django_spire.knowledge.entry.version.block.data.data import BaseEditorBlockData
9
- from django_spire.knowledge.entry.version.block.data.maps import EDITOR_BLOCK_DATA_MAP
8
+ from django_spire.knowledge.entry.version.block.data.data import BaseEditorJsBlockData
9
+ from django_spire.knowledge.entry.version.block.data.maps import EDITOR_JS_BLOCK_DATA_MAP
10
10
  from django_spire.knowledge.entry.version.block.querysets import \
11
11
  EntryVersionBlockQuerySet
12
12
  from django_spire.knowledge.entry.version.block.services.service import \
@@ -40,18 +40,21 @@ class EntryVersionBlock(HistoryModelMixin, OrderingModelMixin):
40
40
  services = EntryVersionBlockService()
41
41
 
42
42
  @property
43
- def editor_block_data(self) -> BaseEditorBlockData:
44
- return EDITOR_BLOCK_DATA_MAP[self.type](**self._block_data)
43
+ def editor_js_block_data(self) -> BaseEditorJsBlockData:
44
+ return EDITOR_JS_BLOCK_DATA_MAP[self.type](**self._block_data)
45
45
 
46
- @editor_block_data.setter
47
- def editor_block_data(self, value: BaseEditorBlockData):
46
+ @editor_js_block_data.setter
47
+ def editor_js_block_data(self, value: BaseEditorJsBlockData):
48
48
  # exclude_none=True ensures that block meta objects aren't autofilled with keys,
49
49
  # which can mess up editor rendering
50
50
  self._block_data = value.model_dump(exclude_none=True)
51
51
  self._text_data = value.render_to_text()
52
52
 
53
+ def update_editor_js_block_data_from_dict(self, value: dict):
54
+ self.editor_js_block_data = EDITOR_JS_BLOCK_DATA_MAP[self.type](**value)
55
+
53
56
  def render_to_text(self) -> str:
54
- return self.editor_block_data.render_to_text()
57
+ return self.editor_js_block_data.render_to_text()
55
58
 
56
59
  class Meta:
57
60
  verbose_name = 'Block'
@@ -4,7 +4,7 @@ from django_spire.contrib.service import BaseDjangoModelService
4
4
 
5
5
  from typing import TYPE_CHECKING
6
6
 
7
- from django_spire.knowledge.entry.version.block.data.maps import EDITOR_BLOCK_DATA_MAP
7
+ from django_spire.knowledge.entry.version.block.data.maps import EDITOR_JS_BLOCK_DATA_MAP
8
8
  from django_spire.knowledge.entry.version.maps import FILE_TYPE_CONVERTER_MAP
9
9
 
10
10
  if TYPE_CHECKING:
@@ -34,17 +34,18 @@ class EntryVersionBlockFactoryService(BaseDjangoModelService['EntryVersionBlock'
34
34
  def create_validated_block(
35
35
  self,
36
36
  entry_version: EntryVersion,
37
- block_type: BlockTypeChoices,
38
- block_data: dict,
39
- block_order: int,
40
- block_tunes: dict = {}
37
+ type: BlockTypeChoices,
38
+ data: dict,
39
+ order: int,
40
+ tunes: dict = {},
41
+ **kwargs,
41
42
  ):
42
43
  self.obj.version = entry_version
43
- self.obj.type = block_type
44
- self.obj.order = block_order
45
- self.obj.tunes = block_tunes
46
- self.obj.editor_block_data = EDITOR_BLOCK_DATA_MAP[block_type](**block_data)
44
+ self.obj.type = type
45
+ self.obj.order = order
46
+ self.obj.tunes = tunes
47
+ self.obj.editor_js_block_data = EDITOR_JS_BLOCK_DATA_MAP[type](**data)
47
48
 
48
49
  self.obj.clean()
49
50
 
50
- return self.obj
51
+ return self.obj
@@ -12,10 +12,11 @@ from django_spire.knowledge.entry.version.tests.factories import \
12
12
 
13
13
  def create_test_block_form_data(**kwargs) -> dict:
14
14
  data = {
15
- 'block_order': 0,
16
- 'block_type': BlockTypeChoices.TEXT,
17
- 'block_data': {'text': 'test text'},
18
- 'block_tunes': {}
15
+ 'id': 'ABC123',
16
+ 'order': 0,
17
+ 'type': BlockTypeChoices.TEXT,
18
+ 'data': {'text': 'test text'},
19
+ 'tunes': {}
19
20
  }
20
21
 
21
22
  data.update(kwargs)
@@ -33,7 +34,7 @@ def create_test_version_block(**kwargs) -> EntryVersionBlock:
33
34
 
34
35
  data.update(kwargs)
35
36
  version_block = EntryVersionBlock.objects.create(**data)
36
- version_block.editor_block_data = TextEditorBlockData(
37
+ version_block.editor_js_block_data = TextEditorBlockData(
37
38
  text='',
38
39
  )
39
40
  version_block.save()
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
6
6
 
7
7
  from markitdown import MarkItDown
8
8
 
9
- from django_spire.knowledge.entry.version.constants import MARKDOWN_AI_CHUNK_SIZE
9
+ from django_spire.knowledge.entry.version.consts import MARKDOWN_AI_CHUNK_SIZE
10
10
  from django_spire.knowledge.entry.version.converters.converter import \
11
11
  BaseConverter
12
12
  from django_spire.knowledge.entry.version.block import models
@@ -3,22 +3,24 @@ from __future__ import annotations
3
3
  from dandy import BaseIntel, Bot, Prompt, recorder_to_html_file
4
4
 
5
5
 
6
- class MarkdownTextIntel(BaseIntel):
7
- text: str
6
+ class MarkdownIntel(BaseIntel):
7
+ markdown_content: str
8
8
 
9
9
 
10
10
  class MarkdownFormatLlmBot(Bot):
11
- llm_intel_class = MarkdownTextIntel
11
+ llm_role = 'Markdown Formater'
12
+ llm_task = 'Improve the markdown formatting of the content provided to make more logical sense.'
13
+ llm_guidelines = (
14
+ Prompt()
15
+ .list([
16
+ 'Make sure the relevant heading text is from a heading with mark down formatting.'
17
+ 'Do not change any of the content only change the formatting as needed.'
18
+ ])
19
+ )
20
+ llm_intel_class = MarkdownIntel
12
21
 
13
22
  @recorder_to_html_file('knowledge_markdown_improvement')
14
23
  def process(self, markdown_content: str) -> str:
15
- markdown_prompt = Prompt()
16
- markdown_prompt.text(
17
- 'Can you improve the markdown formatting? Do NOT add or change any of the '
18
- 'content.'
19
- )
20
- markdown_prompt.line_break()
21
- markdown_prompt.text(markdown_content)
24
+ markdown_intel = self.llm.prompt_to_intel(prompt=markdown_content)
22
25
 
23
- result = self.llm.prompt_to_intel(prompt=markdown_prompt)
24
- return result.text
26
+ return markdown_intel.markdown_content
@@ -12,7 +12,7 @@ from django_spire.knowledge.entry.version import models
12
12
 
13
13
  from django_spire.contrib.seeding import DjangoModelSeeder
14
14
  from django_spire.knowledge.entry.version.block.data.maps import \
15
- EDITOR_BLOCK_DATA_REVERSE_MAP
15
+ EDITOR_JS_BLOCK_DATA_REVERSE_MAP
16
16
  from django_spire.knowledge.entry.version.block.models import EntryVersionBlock
17
17
  from django_spire.knowledge.entry.version.block.seeding.constants import SAFETY_BLOCKS
18
18
  from django_spire.knowledge.entry.version.choices import EntryVersionStatusChoices
@@ -63,10 +63,10 @@ class EntryVersionSeeder(DjangoModelSeeder):
63
63
  for idx, safety_block in enumerate(safety_blocks):
64
64
  version_block = EntryVersionBlock(
65
65
  version=entry_version,
66
- type=EDITOR_BLOCK_DATA_REVERSE_MAP[type(safety_block)],
66
+ type=EDITOR_JS_BLOCK_DATA_REVERSE_MAP[type(safety_block)],
67
67
  order=idx,
68
68
  )
69
- version_block.editor_block_data = safety_block
69
+ version_block.editor_js_block_data = safety_block
70
70
  version_blocks.append(version_block)
71
71
 
72
72
  EntryVersionBlock.objects.bulk_create(version_blocks)
@@ -20,19 +20,45 @@ class EntryVersionProcessorService(BaseDjangoModelService['EntryVersion']):
20
20
  self.obj.published_datetime = localtime()
21
21
  self.obj.save()
22
22
 
23
- def update_blocks(self, raw_block_data: list[dict]):
23
+ def add_update_delete_blocks(self, block_data_list: list[dict]):
24
24
  from django_spire.knowledge.entry.version.block.models import EntryVersionBlock
25
25
 
26
+ entry_blocks_to_add = []
27
+ entry_blocks_to_update = []
28
+ entry_blocks_to_delete = []
29
+
30
+ handled_block_ids = []
31
+
26
32
  old_entry_blocks = self.obj.blocks.active()
27
33
 
28
- incoming_entry_blocks = [
29
- EntryVersionBlock.services.factory.create_validated_block(
30
- entry_version=self.obj,
31
- **block_data,
32
- )
33
- for block_data in raw_block_data
34
- ]
34
+ old_entry_block_ids = [entry_block.id for entry_block in old_entry_blocks]
35
+
36
+ for block_data in block_data_list:
37
+ if block_data['id'] in old_entry_block_ids:
38
+ entry_block: EntryVersionBlock = old_entry_blocks.get(id=block_data['id'])
39
+
40
+ entry_block.type = block_data['type']
41
+ entry_block.order = block_data['order']
42
+ entry_block.update_editor_js_block_data_from_dict(block_data['data'])
43
+
44
+ entry_blocks_to_update.append(entry_block)
45
+
46
+ handled_block_ids.append(block_data['id'])
47
+
48
+ else:
49
+ entry_block = EntryVersionBlock.services.factory.create_validated_block(
50
+ entry_version=self.obj,
51
+ **block_data,
52
+ )
53
+ entry_blocks_to_add.append(entry_block)
54
+
55
+ handled_block_ids.append(block_data['id'])
56
+
57
+ for entry_block in old_entry_blocks:
58
+ if entry_block.id not in handled_block_ids:
59
+ entry_blocks_to_delete.append(entry_block.id)
35
60
 
36
61
  with transaction.atomic():
37
- old_entry_blocks.delete()
38
- EntryVersionBlock.objects.bulk_create(incoming_entry_blocks)
62
+ EntryVersionBlock.objects.filter(id__in=entry_blocks_to_delete).delete()
63
+ EntryVersionBlock.objects.bulk_update(entry_blocks_to_update, ['order', 'type', '_block_data', '_text_data'])
64
+ EntryVersionBlock.objects.bulk_create(entry_blocks_to_add)
@@ -5,7 +5,7 @@ import time
5
5
  from unittest.mock import MagicMock, patch
6
6
 
7
7
  from django_spire.core.tests.test_cases import BaseTestCase
8
- from django_spire.knowledge.entry.version.constants import MARKDOWN_AI_CHUNK_SIZE
8
+ from django_spire.knowledge.entry.version.consts import MARKDOWN_AI_CHUNK_SIZE
9
9
  from django_spire.knowledge.entry.version.converters.docx_converter import DocxConverter
10
10
 
11
11
  from django_spire.knowledge.entry.version.tests.factories import \
@@ -22,7 +22,7 @@ class EntryVersionPageUrlsTests(BaseTestCase):
22
22
  def test_detail_view_url_path(self):
23
23
  response = self.client.get(
24
24
  reverse(
25
- 'django_spire:knowledge:entry:version:page:detail',
25
+ 'django_spire:knowledge:entry:version:page:editor',
26
26
  kwargs={'pk': self.test_entry_version.pk}
27
27
  )
28
28
  )
@@ -6,5 +6,5 @@ from django_spire.knowledge.entry.version.views import page_views
6
6
  app_name = 'page'
7
7
 
8
8
  urlpatterns = [
9
- path('<int:pk>/detail/', page_views.detail_view, name='detail'),
9
+ path('<int:pk>/editor/', page_views.editor_view, name='editor'),
10
10
  ]
@@ -15,7 +15,9 @@ from django_spire.knowledge.entry.version.models import EntryVersion
15
15
  @AppAuthController('knowledge').permission_required('can_change')
16
16
  def update_blocks_view(request: WSGIRequest, pk: int) -> JsonResponse:
17
17
  entry_version = get_object_or_404(EntryVersion.objects.prefetch_blocks(), pk=pk)
18
- new_block_data = json.loads(request.body.decode('utf-8'))
19
- entry_version.services.processor.update_blocks(new_block_data)
20
18
 
21
- return JsonResponse({'type': 'success'})
19
+ block_data_list = json.loads(request.body.decode('utf-8'))
20
+
21
+ entry_version.services.processor.add_update_delete_blocks(block_data_list)
22
+
23
+ return JsonResponse({'type': 'success'})
@@ -3,22 +3,22 @@ from __future__ import annotations
3
3
  import json
4
4
 
5
5
  from django.core.handlers.wsgi import WSGIRequest
6
- from django.db.models import F, Prefetch
7
6
  from django.shortcuts import get_object_or_404
8
7
  from django.template.response import TemplateResponse
9
8
  from django.urls import reverse
10
9
 
11
10
  from django_spire.auth.controller.controller import AppAuthController
12
11
  from django_spire.contrib.generic_views import portal_views
13
- from django_spire.knowledge.entry.version.block.models import EntryVersionBlock
12
+ from django_spire.knowledge.collection.models import Collection
14
13
  from django_spire.knowledge.entry.version.models import EntryVersion
15
14
 
16
15
 
17
16
  @AppAuthController('knowledge').permission_required('can_view')
18
- def detail_view(request: WSGIRequest, pk: int) -> TemplateResponse:
17
+ def editor_view(request: WSGIRequest, pk: int) -> TemplateResponse:
19
18
  entry_version = get_object_or_404(EntryVersion.objects.prefetch_blocks(),pk=pk)
20
19
 
21
20
  entry = entry_version.entry
21
+ top_level_collection = entry.top_level_collection
22
22
  version_blocks = entry_version.blocks.format_for_editor()
23
23
 
24
24
  def breadcrumbs_func(breadcrumbs):
@@ -26,13 +26,7 @@ def detail_view(request: WSGIRequest, pk: int) -> TemplateResponse:
26
26
  name='Knowledge',
27
27
  href=reverse('django_spire:knowledge:page:home')
28
28
  )
29
- collection = entry.collection
30
- breadcrumbs.add_breadcrumb(
31
- name=f'{collection.name}'
32
- )
33
- breadcrumbs.add_breadcrumb(
34
- name=f'{entry.name}',
35
- )
29
+ breadcrumbs.add_base_breadcrumb(entry)
36
30
 
37
31
  return portal_views.detail_view(
38
32
  request,
@@ -40,8 +34,13 @@ def detail_view(request: WSGIRequest, pk: int) -> TemplateResponse:
40
34
  breadcrumbs_func=breadcrumbs_func,
41
35
  context_data={
42
36
  'entry': entry,
43
- 'version': entry_version,
37
+ 'current_version': entry_version,
38
+ 'collection': top_level_collection,
44
39
  'version_blocks': json.dumps(list(version_blocks)),
40
+ 'collection_tree_json': Collection.services.transformation.to_hierarchy_json(
41
+ request=request,
42
+ parent_id=top_level_collection.id,
43
+ ),
45
44
  },
46
- template='django_spire/knowledge/entry/version/page/detail_page.html',
45
+ template='django_spire/knowledge/entry/version/page/editor_page.html',
47
46
  )
@@ -16,7 +16,7 @@ def publish_view(request: WSGIRequest, pk: int) -> HttpResponseRedirect:
16
16
 
17
17
  return HttpResponseRedirect(
18
18
  reverse(
19
- 'django_spire:knowledge:entry:version:page:detail',
19
+ 'django_spire:knowledge:entry:version:page:editor',
20
20
  kwargs={'pk': version.pk}
21
21
  )
22
22
  )
@@ -37,7 +37,7 @@ def form_view(
37
37
 
38
38
  return HttpResponseRedirect(
39
39
  reverse(
40
- 'django_spire:knowledge:entry:version:page:detail',
40
+ 'django_spire:knowledge:entry:version:page:editor',
41
41
  kwargs={'pk': entry.pk}
42
42
  )
43
43
  )
@@ -1,23 +1,45 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dandy import Bot, Prompt
4
- from dandy.intel.intel import DefaultIntel
5
4
 
6
5
  from django_spire.knowledge.entry.models import Entry
6
+ from django_spire.knowledge.intelligence.intel.collection_intel import CollectionIntel
7
+ from django_spire.knowledge.intelligence.intel.entry_intel import EntryIntel
7
8
 
8
9
 
9
- class EntrySearchLlmBot(Bot):
10
- @classmethod
11
- def process(cls, user_input: str, entry: Entry) -> str:
10
+ class EntrySearchBot(Bot):
11
+ llm_role = 'Knowledge Entry Search Assistant'
12
+ llm_task = 'Read through the knowledge entry the information request and return information and the block id that relevant'
13
+ llm_guidelines = (
14
+ Prompt()
15
+ .list([
16
+ 'Make sure the relevant heading text is from a heading with mark down formatting.'
17
+ 'When returning the relevant heading remove any of the markdown formating characters.'
18
+ ])
19
+ )
20
+ llm_intel_class = EntryIntel
21
+
22
+ def process(self, user_input: str, entry: Entry) -> EntryIntel:
12
23
  entry_prompt = Prompt()
13
- entry_prompt.text('You are a helpful assistant that helps users find information about entries.')
14
- entry_prompt.text(f'User Input: {user_input}')
15
- entry_prompt.text(f'Entry: {entry.name}')
24
+ entry_prompt.sub_heading('Information Request')
25
+ entry_prompt.line_break()
26
+ entry_prompt.text(f'{user_input}')
27
+ entry_prompt.line_break()
28
+ entry_prompt.sub_heading(f'Knowledge Entry: {entry.name}')
29
+ entry_prompt.line_break()
16
30
 
17
31
  for version_block in entry.current_version.blocks.all():
18
- entry_prompt.text(version_block.render_to_text())
32
+ entry_prompt.text(f'{version_block.id}: {version_block.render_to_text()}')
19
33
 
20
- return cls().llm.prompt_to_intel(
34
+ entry_intel = self.llm.prompt_to_intel(
21
35
  prompt=entry_prompt,
22
- intel_class=DefaultIntel
23
- ).content
36
+ exclude_fields={'entry_id', 'collection_intel'},
37
+ )
38
+
39
+ entry_intel.entry_id = entry.id
40
+ entry_intel.collection_intel = CollectionIntel(
41
+ name=entry.collection.name,
42
+ collection_id=entry.collection.id,
43
+ )
44
+
45
+ return entry_intel
@@ -5,15 +5,15 @@ from dandy import Decoder
5
5
  from django_spire.knowledge.collection.models import Collection
6
6
 
7
7
 
8
- def get_collection_map_class() -> type[Decoder]:
9
- class CollectionMap(Decoder):
8
+ def get_collection_decoder() -> Decoder:
9
+ class CollectionDecoder(Decoder):
10
10
  mapping_keys_description = 'Knowledge Collection Titles'
11
11
  mapping = {
12
12
  **{
13
- collection.name: collection
13
+ f'{collection.name}: {collection.description}': collection
14
14
  for collection in Collection.objects.all().annotate_entry_count()
15
15
  },
16
- 'No Matching Knowledge Collection Titles': None
16
+ 'No Matching Knowledge Collection Titles': None,
17
17
  }
18
18
 
19
- return CollectionMap
19
+ return CollectionDecoder()
@@ -5,8 +5,8 @@ from dandy import Decoder
5
5
  from django_spire.knowledge.collection.models import Collection
6
6
 
7
7
 
8
- def get_entry_map_class(collection: Collection) -> type[Decoder]:
9
- class EntryMap(Decoder):
8
+ def get_entry_decoder(collection: Collection) -> Decoder:
9
+ class EntryDecoder(Decoder):
10
10
  mapping_keys_description = 'Knowledge Entries'
11
11
  mapping = {
12
12
  **{
@@ -16,4 +16,4 @@ def get_entry_map_class(collection: Collection) -> type[Decoder]:
16
16
  'No Matching Knowledge Entries': None
17
17
  }
18
18
 
19
- return EntryMap
19
+ return EntryDecoder()
@@ -5,3 +5,4 @@ from dandy import BaseIntel
5
5
 
6
6
  class CollectionIntel(BaseIntel):
7
7
  name: str
8
+ collection_id: int = None
@@ -6,8 +6,11 @@ from django_spire.knowledge.intelligence.intel.collection_intel import Collectio
6
6
 
7
7
 
8
8
  class EntryIntel(BaseIntel):
9
- body: str
10
- collection_intel: CollectionIntel
9
+ relevant_heading_text: str
10
+ relevant_subject_text: str
11
+ relevant_block_id: int
12
+ entry_id: int = None
13
+ collection_intel: CollectionIntel = None
11
14
 
12
15
 
13
16
  class EntriesIntel(BaseListIntel[EntryIntel]):
@@ -1,11 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from django_spire.ai.chat.message_intel import BaseMessageIntel
4
+ from django_spire.knowledge.intelligence.intel.entry_intel import EntriesIntel
4
5
 
5
6
 
6
7
  class KnowledgeMessageIntel(BaseMessageIntel):
7
8
  _template: str = 'django_spire/knowledge/message/knowledge_message_intel.html'
8
9
  body: str
10
+ entries_intel: EntriesIntel
9
11
 
10
12
  def content_to_str(self) -> str:
11
13
  return self.body