django-cfg 1.1.82__py3-none-any.whl → 1.2.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 (244) hide show
  1. django_cfg/__init__.py +20 -448
  2. django_cfg/apps/accounts/README.md +3 -3
  3. django_cfg/apps/accounts/admin/__init__.py +0 -2
  4. django_cfg/apps/accounts/admin/activity.py +2 -9
  5. django_cfg/apps/accounts/admin/filters.py +0 -42
  6. django_cfg/apps/accounts/admin/inlines.py +8 -8
  7. django_cfg/apps/accounts/admin/otp.py +5 -5
  8. django_cfg/apps/accounts/admin/registration_source.py +1 -8
  9. django_cfg/apps/accounts/admin/user.py +12 -20
  10. django_cfg/apps/accounts/managers/user_manager.py +2 -129
  11. django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
  12. django_cfg/apps/accounts/models.py +3 -123
  13. django_cfg/apps/accounts/serializers/otp.py +40 -44
  14. django_cfg/apps/accounts/serializers/profile.py +0 -2
  15. django_cfg/apps/accounts/services/otp_service.py +98 -186
  16. django_cfg/apps/accounts/signals.py +25 -15
  17. django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
  18. django_cfg/apps/accounts/views/otp.py +35 -36
  19. django_cfg/apps/agents/README.md +129 -0
  20. django_cfg/apps/agents/__init__.py +68 -0
  21. django_cfg/apps/agents/admin/__init__.py +17 -0
  22. django_cfg/apps/agents/admin/execution_admin.py +460 -0
  23. django_cfg/apps/agents/admin/registry_admin.py +360 -0
  24. django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
  25. django_cfg/apps/agents/apps.py +29 -0
  26. django_cfg/apps/agents/core/__init__.py +20 -0
  27. django_cfg/apps/agents/core/agent.py +281 -0
  28. django_cfg/apps/agents/core/dependencies.py +154 -0
  29. django_cfg/apps/agents/core/exceptions.py +66 -0
  30. django_cfg/apps/agents/core/models.py +106 -0
  31. django_cfg/apps/agents/core/orchestrator.py +391 -0
  32. django_cfg/apps/agents/examples/__init__.py +3 -0
  33. django_cfg/apps/agents/examples/simple_example.py +161 -0
  34. django_cfg/apps/agents/integration/__init__.py +14 -0
  35. django_cfg/apps/agents/integration/middleware.py +80 -0
  36. django_cfg/apps/agents/integration/registry.py +345 -0
  37. django_cfg/apps/agents/integration/signals.py +50 -0
  38. django_cfg/apps/agents/management/__init__.py +3 -0
  39. django_cfg/apps/agents/management/commands/__init__.py +3 -0
  40. django_cfg/apps/agents/management/commands/create_agent.py +365 -0
  41. django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
  42. django_cfg/apps/agents/managers/__init__.py +23 -0
  43. django_cfg/apps/agents/managers/execution.py +236 -0
  44. django_cfg/apps/agents/managers/registry.py +254 -0
  45. django_cfg/apps/agents/managers/toolsets.py +496 -0
  46. django_cfg/apps/agents/migrations/0001_initial.py +286 -0
  47. django_cfg/apps/agents/migrations/__init__.py +5 -0
  48. django_cfg/apps/agents/models/__init__.py +15 -0
  49. django_cfg/apps/agents/models/execution.py +215 -0
  50. django_cfg/apps/agents/models/registry.py +220 -0
  51. django_cfg/apps/agents/models/toolsets.py +305 -0
  52. django_cfg/apps/agents/patterns/__init__.py +24 -0
  53. django_cfg/apps/agents/patterns/content_agents.py +234 -0
  54. django_cfg/apps/agents/toolsets/__init__.py +15 -0
  55. django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
  56. django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
  57. django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
  58. django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
  59. django_cfg/apps/agents/urls.py +46 -0
  60. django_cfg/apps/knowbase/README.md +150 -0
  61. django_cfg/apps/knowbase/__init__.py +27 -0
  62. django_cfg/apps/knowbase/admin/__init__.py +23 -0
  63. django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
  64. django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
  65. django_cfg/apps/knowbase/admin/document_admin.py +650 -0
  66. django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
  67. django_cfg/apps/knowbase/apps.py +81 -0
  68. django_cfg/apps/knowbase/config/README.md +176 -0
  69. django_cfg/apps/knowbase/config/__init__.py +51 -0
  70. django_cfg/apps/knowbase/config/constance_fields.py +186 -0
  71. django_cfg/apps/knowbase/config/constance_settings.py +200 -0
  72. django_cfg/apps/knowbase/config/settings.py +444 -0
  73. django_cfg/apps/knowbase/examples/__init__.py +3 -0
  74. django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
  75. django_cfg/apps/knowbase/management/__init__.py +0 -0
  76. django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
  77. django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
  78. django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
  79. django_cfg/apps/knowbase/managers/__init__.py +22 -0
  80. django_cfg/apps/knowbase/managers/archive.py +426 -0
  81. django_cfg/apps/knowbase/managers/base.py +32 -0
  82. django_cfg/apps/knowbase/managers/chat.py +141 -0
  83. django_cfg/apps/knowbase/managers/document.py +203 -0
  84. django_cfg/apps/knowbase/managers/external_data.py +471 -0
  85. django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
  86. django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
  87. django_cfg/apps/knowbase/migrations/__init__.py +5 -0
  88. django_cfg/apps/knowbase/mixins/__init__.py +15 -0
  89. django_cfg/apps/knowbase/mixins/config.py +108 -0
  90. django_cfg/apps/knowbase/mixins/creator.py +81 -0
  91. django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
  92. django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
  93. django_cfg/apps/knowbase/mixins/service.py +362 -0
  94. django_cfg/apps/knowbase/models/__init__.py +41 -0
  95. django_cfg/apps/knowbase/models/archive.py +599 -0
  96. django_cfg/apps/knowbase/models/base.py +58 -0
  97. django_cfg/apps/knowbase/models/chat.py +157 -0
  98. django_cfg/apps/knowbase/models/document.py +267 -0
  99. django_cfg/apps/knowbase/models/external_data.py +376 -0
  100. django_cfg/apps/knowbase/serializers/__init__.py +68 -0
  101. django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
  102. django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
  103. django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
  104. django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
  105. django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
  106. django_cfg/apps/knowbase/services/__init__.py +40 -0
  107. django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
  108. django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
  109. django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
  110. django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
  111. django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
  112. django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
  113. django_cfg/apps/knowbase/services/base.py +53 -0
  114. django_cfg/apps/knowbase/services/chat_service.py +239 -0
  115. django_cfg/apps/knowbase/services/document_service.py +144 -0
  116. django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
  117. django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
  118. django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
  119. django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
  120. django_cfg/apps/knowbase/services/embedding/models.py +229 -0
  121. django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
  122. django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
  123. django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
  124. django_cfg/apps/knowbase/services/search_service.py +293 -0
  125. django_cfg/apps/knowbase/signals/__init__.py +21 -0
  126. django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
  127. django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
  128. django_cfg/apps/knowbase/signals/document_signals.py +143 -0
  129. django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
  130. django_cfg/apps/knowbase/tasks/__init__.py +39 -0
  131. django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
  132. django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
  133. django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
  134. django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
  135. django_cfg/apps/knowbase/urls.py +43 -0
  136. django_cfg/apps/knowbase/utils/__init__.py +12 -0
  137. django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
  138. django_cfg/apps/knowbase/utils/text_processing.py +375 -0
  139. django_cfg/apps/knowbase/utils/validation.py +99 -0
  140. django_cfg/apps/knowbase/views/__init__.py +28 -0
  141. django_cfg/apps/knowbase/views/archive_views.py +469 -0
  142. django_cfg/apps/knowbase/views/base.py +49 -0
  143. django_cfg/apps/knowbase/views/chat_views.py +181 -0
  144. django_cfg/apps/knowbase/views/document_views.py +183 -0
  145. django_cfg/apps/knowbase/views/public_views.py +129 -0
  146. django_cfg/apps/leads/admin.py +70 -0
  147. django_cfg/apps/newsletter/admin.py +234 -0
  148. django_cfg/apps/newsletter/admin_filters.py +124 -0
  149. django_cfg/apps/support/admin.py +196 -0
  150. django_cfg/apps/support/admin_filters.py +71 -0
  151. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  152. django_cfg/apps/urls.py +5 -4
  153. django_cfg/cli/README.md +1 -1
  154. django_cfg/cli/commands/create_project.py +2 -2
  155. django_cfg/cli/commands/info.py +1 -1
  156. django_cfg/config.py +44 -0
  157. django_cfg/core/config.py +29 -82
  158. django_cfg/core/environment.py +1 -1
  159. django_cfg/core/generation.py +19 -107
  160. django_cfg/{integration.py → core/integration.py} +18 -16
  161. django_cfg/core/validation.py +1 -1
  162. django_cfg/management/__init__.py +1 -1
  163. django_cfg/management/commands/__init__.py +1 -1
  164. django_cfg/management/commands/auto_generate.py +482 -0
  165. django_cfg/management/commands/migrator.py +19 -101
  166. django_cfg/management/commands/test_email.py +1 -1
  167. django_cfg/middleware/README.md +0 -158
  168. django_cfg/middleware/__init__.py +0 -2
  169. django_cfg/middleware/user_activity.py +3 -3
  170. django_cfg/models/api.py +145 -0
  171. django_cfg/models/base.py +287 -0
  172. django_cfg/models/cache.py +4 -4
  173. django_cfg/models/constance.py +25 -88
  174. django_cfg/models/database.py +9 -9
  175. django_cfg/models/drf.py +3 -36
  176. django_cfg/models/email.py +163 -0
  177. django_cfg/models/environment.py +276 -0
  178. django_cfg/models/limits.py +1 -1
  179. django_cfg/models/logging.py +366 -0
  180. django_cfg/models/revolution.py +41 -2
  181. django_cfg/models/security.py +125 -0
  182. django_cfg/models/services.py +1 -1
  183. django_cfg/modules/__init__.py +2 -56
  184. django_cfg/modules/base.py +78 -52
  185. django_cfg/modules/django_currency/service.py +2 -2
  186. django_cfg/modules/django_email.py +2 -2
  187. django_cfg/modules/django_health.py +267 -0
  188. django_cfg/modules/django_llm/llm/client.py +79 -17
  189. django_cfg/modules/django_llm/translator/translator.py +2 -2
  190. django_cfg/modules/django_logger.py +2 -2
  191. django_cfg/modules/django_ngrok.py +2 -2
  192. django_cfg/modules/django_tasks.py +68 -3
  193. django_cfg/modules/django_telegram.py +3 -3
  194. django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
  195. django_cfg/modules/django_twilio/service.py +2 -2
  196. django_cfg/modules/django_twilio/simple_service.py +2 -2
  197. django_cfg/modules/django_twilio/twilio_service.py +2 -2
  198. django_cfg/modules/django_unfold/__init__.py +69 -0
  199. django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
  200. django_cfg/modules/django_unfold/dashboard.py +278 -0
  201. django_cfg/modules/django_unfold/icons/README.md +145 -0
  202. django_cfg/modules/django_unfold/icons/__init__.py +12 -0
  203. django_cfg/modules/django_unfold/icons/constants.py +2851 -0
  204. django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
  205. django_cfg/modules/django_unfold/models/__init__.py +42 -0
  206. django_cfg/modules/django_unfold/models/config.py +601 -0
  207. django_cfg/modules/django_unfold/models/dashboard.py +206 -0
  208. django_cfg/modules/django_unfold/models/dropdown.py +40 -0
  209. django_cfg/modules/django_unfold/models/navigation.py +73 -0
  210. django_cfg/modules/django_unfold/models/tabs.py +25 -0
  211. django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
  212. django_cfg/modules/django_unfold/utils.py +140 -0
  213. django_cfg/registry/__init__.py +23 -0
  214. django_cfg/registry/core.py +61 -0
  215. django_cfg/registry/exceptions.py +11 -0
  216. django_cfg/registry/modules.py +12 -0
  217. django_cfg/registry/services.py +26 -0
  218. django_cfg/registry/third_party.py +52 -0
  219. django_cfg/routing/__init__.py +19 -0
  220. django_cfg/routing/callbacks.py +198 -0
  221. django_cfg/routing/routers.py +48 -0
  222. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
  223. django_cfg/templatetags/__init__.py +0 -0
  224. django_cfg/templatetags/django_cfg.py +33 -0
  225. django_cfg/urls.py +33 -0
  226. django_cfg/utils/path_resolution.py +1 -1
  227. django_cfg/utils/smart_defaults.py +7 -61
  228. django_cfg/utils/toolkit.py +663 -0
  229. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
  230. django_cfg-1.2.0.dist-info/RECORD +441 -0
  231. django_cfg/archive/django_sample.zip +0 -0
  232. django_cfg/models/unfold.py +0 -271
  233. django_cfg/modules/unfold/__init__.py +0 -29
  234. django_cfg/modules/unfold/dashboard.py +0 -318
  235. django_cfg/pyproject.toml +0 -370
  236. django_cfg/routers.py +0 -83
  237. django_cfg-1.1.82.dist-info/RECORD +0 -278
  238. /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
  239. /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
  240. /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
  241. /django_cfg/{version_check.py → utils/version_check.py} +0 -0
  242. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
  243. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
  244. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,376 @@
1
+ """
2
+ External Data models for integrating external Django models into django_cfg.apps.knowbase.
3
+
4
+ Provides a unified way to vectorize and search any external data source.
5
+ """
6
+
7
+ from django.db import models
8
+ from django.conf import settings
9
+ from pgvector.django import VectorField
10
+ from typing import Optional, List, Dict, Any
11
+ import json
12
+ from enum import Enum
13
+
14
+ from .base import UserScopedModel, ProcessingStatus, TimestampedModel
15
+ from .document import DocumentCategory
16
+
17
+
18
+ class ExternalDataType(models.TextChoices):
19
+ """Types of external data sources."""
20
+ MODEL = "model", "Django Model"
21
+ API = "api", "API Endpoint"
22
+ DATABASE = "database", "Database Query"
23
+ FILE = "file", "File System"
24
+ CUSTOM = "custom", "Custom Source"
25
+
26
+
27
+ class ExternalDataStatus(models.TextChoices):
28
+ """Processing status for external data."""
29
+ PENDING = "pending", "Pending"
30
+ PROCESSING = "processing", "Processing"
31
+ COMPLETED = "completed", "Completed"
32
+ FAILED = "failed", "Failed"
33
+ OUTDATED = "outdated", "Outdated"
34
+
35
+
36
+ class ExternalData(UserScopedModel):
37
+ """
38
+ External data source integrated into knowbase for AI search.
39
+
40
+ This model represents any external data source (Django models, APIs, etc.)
41
+ that has been integrated into the knowledge base for semantic search.
42
+ """
43
+
44
+ # Manager will be set after class definition
45
+ from ..managers.external_data import ExternalDataManager
46
+ objects = ExternalDataManager()
47
+
48
+ # Basic information
49
+ title = models.CharField(
50
+ max_length=512,
51
+ help_text="Human-readable title for this external data source"
52
+ )
53
+ description = models.TextField(
54
+ blank=True,
55
+ help_text="Description of what this external data contains"
56
+ )
57
+
58
+ # Source configuration
59
+ source_type = models.CharField(
60
+ max_length=20,
61
+ choices=ExternalDataType.choices,
62
+ default=ExternalDataType.MODEL,
63
+ help_text="Type of external data source"
64
+ )
65
+ source_identifier = models.CharField(
66
+ max_length=255,
67
+ blank=True,
68
+ help_text="Unique identifier for the data source (e.g., 'vehicles_data.Vehicle')"
69
+ )
70
+ source_config = models.JSONField(
71
+ default=dict,
72
+ blank=True,
73
+ help_text="Configuration for data extraction (fields, filters, etc.)"
74
+ )
75
+
76
+ # Content and metadata
77
+ content = models.TextField(
78
+ blank=True,
79
+ help_text="Extracted text content for vectorization"
80
+ )
81
+ content_hash = models.CharField(
82
+ max_length=64,
83
+ blank=True,
84
+ db_index=True,
85
+ help_text="SHA256 hash of content for change detection"
86
+ )
87
+ metadata = models.JSONField(
88
+ default=dict,
89
+ blank=True,
90
+ help_text="Additional metadata from the source"
91
+ )
92
+
93
+ # Processing status
94
+ status = models.CharField(
95
+ max_length=20,
96
+ choices=ExternalDataStatus.choices,
97
+ default=ExternalDataStatus.PENDING,
98
+ help_text="Current processing status"
99
+ )
100
+ processing_error = models.TextField(
101
+ blank=True,
102
+ help_text="Error message if processing failed"
103
+ )
104
+
105
+ # Vectorization settings
106
+ chunk_size = models.PositiveIntegerField(
107
+ default=1000,
108
+ help_text="Size of text chunks for vectorization"
109
+ )
110
+ overlap_size = models.PositiveIntegerField(
111
+ default=200,
112
+ help_text="Overlap between chunks"
113
+ )
114
+ embedding_model = models.CharField(
115
+ max_length=100,
116
+ default="text-embedding-ada-002",
117
+ help_text="Embedding model used for vectorization"
118
+ )
119
+
120
+ # Search settings
121
+ similarity_threshold = models.FloatField(
122
+ default=0.5,
123
+ help_text="Similarity threshold for this external data (0.0-1.0). Lower = more results, higher = more precise"
124
+ )
125
+
126
+ # Processing timestamps
127
+ processed_at = models.DateTimeField(
128
+ null=True, blank=True,
129
+ help_text="When the data was last processed"
130
+ )
131
+ source_updated_at = models.DateTimeField(
132
+ null=True, blank=True,
133
+ help_text="When the source data was last updated"
134
+ )
135
+
136
+ # Statistics
137
+ total_chunks = models.PositiveIntegerField(
138
+ default=0,
139
+ help_text="Total number of chunks created"
140
+ )
141
+ total_tokens = models.PositiveIntegerField(
142
+ default=0,
143
+ help_text="Total tokens processed"
144
+ )
145
+ processing_cost = models.FloatField(
146
+ default=0.0,
147
+ help_text="Total cost for processing this data (USD)"
148
+ )
149
+
150
+ # Organization
151
+ category = models.ForeignKey(
152
+ DocumentCategory,
153
+ on_delete=models.SET_NULL,
154
+ null=True, blank=True,
155
+ help_text="Category for organization"
156
+ )
157
+ tags = models.JSONField(
158
+ default=list,
159
+ blank=True,
160
+ help_text="Tags for categorization and filtering"
161
+ )
162
+
163
+ # Access control
164
+ is_active = models.BooleanField(
165
+ default=True,
166
+ help_text="Whether this data source is active for search"
167
+ )
168
+ is_public = models.BooleanField(
169
+ default=False,
170
+ help_text="Whether this data is publicly searchable"
171
+ )
172
+
173
+ class Meta:
174
+ db_table = 'django_cfg_knowbase_external_data'
175
+ verbose_name = 'External Data'
176
+ verbose_name_plural = 'External Data'
177
+ unique_together = [['user', 'source_identifier']]
178
+ indexes = [
179
+ models.Index(fields=['user', 'source_type']),
180
+ models.Index(fields=['status']),
181
+ models.Index(fields=['is_active']),
182
+ models.Index(fields=['processed_at']),
183
+ models.Index(fields=['source_identifier']),
184
+ models.Index(fields=['content_hash']),
185
+ ]
186
+ ordering = ['-processed_at', '-created_at']
187
+
188
+ def save(self, *args, **kwargs):
189
+ """Override save to generate content_hash if not provided."""
190
+ # Store original hash for comparison in signals
191
+ if self.pk:
192
+ try:
193
+ original = ExternalData.objects.get(pk=self.pk)
194
+ self._original_content_hash = original.content_hash
195
+ except ExternalData.DoesNotExist:
196
+ self._original_content_hash = None
197
+ else:
198
+ self._original_content_hash = None
199
+
200
+ # Generate hash if not provided
201
+ if not self.content_hash and self.content:
202
+ import hashlib
203
+ self.content_hash = hashlib.sha256(self.content.encode()).hexdigest()
204
+ elif self.content:
205
+ # Regenerate hash if content exists (to catch manual content changes)
206
+ import hashlib
207
+ new_hash = hashlib.sha256(self.content.encode()).hexdigest()
208
+ if self.content_hash != new_hash:
209
+ self.content_hash = new_hash
210
+
211
+ super().save(*args, **kwargs)
212
+
213
+ def __str__(self) -> str:
214
+ return f"{self.title} ({self.source_type})"
215
+
216
+ @property
217
+ def full_name(self) -> str:
218
+ """Full name including source type."""
219
+ return f"{self.title} ({self.get_source_type_display()})"
220
+
221
+ @property
222
+ def is_processed(self) -> bool:
223
+ """Check if data has been successfully processed."""
224
+ return self.status == ExternalDataStatus.COMPLETED
225
+
226
+ @property
227
+ def is_outdated(self) -> bool:
228
+ """Check if data needs reprocessing."""
229
+ return (
230
+ self.status == ExternalDataStatus.OUTDATED or
231
+ (self.source_updated_at and self.processed_at and
232
+ self.source_updated_at > self.processed_at)
233
+ )
234
+
235
+ @property
236
+ def chunks_count(self) -> int:
237
+ """Get the number of chunks for this external data."""
238
+ return self.chunks.count()
239
+
240
+ def get_config_value(self, key: str, default=None):
241
+ """Get a configuration value."""
242
+ return self.source_config.get(key, default)
243
+
244
+ def set_config_value(self, key: str, value):
245
+ """Set a configuration value."""
246
+ self.source_config[key] = value
247
+ self.save(update_fields=['source_config'])
248
+
249
+ def add_tag(self, tag: str):
250
+ """Add a tag if it doesn't exist."""
251
+ if tag not in self.tags:
252
+ self.tags.append(tag)
253
+ self.save(update_fields=['tags'])
254
+
255
+ def remove_tag(self, tag: str):
256
+ """Remove a tag if it exists."""
257
+ if tag in self.tags:
258
+ self.tags.remove(tag)
259
+ self.save(update_fields=['tags'])
260
+
261
+
262
+ class ExternalDataChunk(TimestampedModel):
263
+ """
264
+ Vectorized chunk of external data content.
265
+
266
+ Similar to DocumentChunk but for external data sources.
267
+ """
268
+
269
+ # Manager will be set after class definition
270
+ objects = models.Manager() # Temporary default manager
271
+
272
+ # Relationships
273
+ user = models.ForeignKey(
274
+ settings.AUTH_USER_MODEL,
275
+ on_delete=models.CASCADE,
276
+ db_index=True,
277
+ help_text="Owner of this chunk"
278
+ )
279
+ external_data = models.ForeignKey(
280
+ ExternalData,
281
+ on_delete=models.CASCADE,
282
+ related_name='chunks',
283
+ help_text="External data this chunk belongs to"
284
+ )
285
+
286
+ # Content
287
+ content = models.TextField(
288
+ blank=True,
289
+ help_text="Text content of the chunk"
290
+ )
291
+ chunk_index = models.PositiveIntegerField(
292
+ default=0,
293
+ help_text="Sequential index of this chunk within the external data"
294
+ )
295
+
296
+ # Vector embedding
297
+ embedding = VectorField(
298
+ dimensions=1536, # OpenAI ada-002 default
299
+ null=True, blank=True,
300
+ help_text="Vector embedding for semantic search"
301
+ )
302
+ embedding_model = models.CharField(
303
+ max_length=100,
304
+ default="text-embedding-ada-002",
305
+ help_text="Model used for embedding generation"
306
+ )
307
+
308
+ # Metrics
309
+ token_count = models.PositiveIntegerField(
310
+ default=0,
311
+ help_text="Number of tokens in this chunk"
312
+ )
313
+ character_count = models.PositiveIntegerField(
314
+ default=0,
315
+ help_text="Number of characters in this chunk"
316
+ )
317
+ embedding_cost = models.FloatField(
318
+ default=0.0,
319
+ help_text="Cost for generating this embedding (USD)"
320
+ )
321
+
322
+ # Context information
323
+ chunk_metadata = models.JSONField(
324
+ default=dict,
325
+ blank=True,
326
+ help_text="Additional metadata for this specific chunk"
327
+ )
328
+
329
+ class Meta:
330
+ db_table = 'django_cfg_knowbase_external_data_chunk'
331
+ verbose_name = 'External Data Chunk'
332
+ verbose_name_plural = 'External Data Chunks'
333
+ unique_together = [['external_data', 'chunk_index']]
334
+ indexes = [
335
+ models.Index(fields=['user', 'external_data']),
336
+ models.Index(fields=['embedding_model']),
337
+ models.Index(fields=['token_count']),
338
+ models.Index(fields=['chunk_index']),
339
+ ]
340
+ ordering = ['external_data', 'chunk_index']
341
+
342
+ def __str__(self) -> str:
343
+ return f"{self.external_data.title} - Chunk {self.chunk_index}"
344
+
345
+ @property
346
+ def content_preview(self) -> str:
347
+ """Get a preview of the chunk content."""
348
+ if not self.content:
349
+ return "No content"
350
+ if len(self.content) <= 100:
351
+ return self.content
352
+ return self.content[:100] + "..."
353
+
354
+ @property
355
+ def embedding_info(self) -> str:
356
+ """Get embedding information."""
357
+ if not self.embedding:
358
+ return "No embedding"
359
+ return f"{len(self.embedding)} dimensions"
360
+
361
+ @property
362
+ def similarity_search_ready(self) -> bool:
363
+ """Check if chunk is ready for similarity search."""
364
+ return self.embedding is not None
365
+
366
+ @property
367
+ def source_info(self) -> Dict[str, Any]:
368
+ """Get source information for this chunk."""
369
+ return {
370
+ 'source_type': self.external_data.source_type,
371
+ 'source_identifier': self.external_data.source_identifier,
372
+ 'title': self.external_data.title,
373
+ 'chunk_index': self.chunk_index,
374
+ 'total_chunks': self.external_data.total_chunks,
375
+ }
376
+
@@ -0,0 +1,68 @@
1
+ """
2
+ Knowledge Base Serializers
3
+
4
+ Pydantic v2 models for API request/response validation.
5
+ """
6
+
7
+ from .document_serializers import *
8
+ from .chat_serializers import *
9
+ from .public_serializers import *
10
+ from .archive_serializers import *
11
+ from .external_data_serializers import *
12
+
13
+ __all__ = [
14
+ # Document serializers
15
+ 'DocumentCreateSerializer',
16
+ 'DocumentSerializer',
17
+ 'DocumentStatsSerializer',
18
+ 'DocumentProcessingStatusSerializer',
19
+
20
+ # Public serializers
21
+ 'PublicCategorySerializer',
22
+ 'PublicDocumentListSerializer',
23
+ 'PublicDocumentSerializer',
24
+
25
+ # Chat serializers
26
+ 'ChatSessionCreateRequest',
27
+ 'ChatSessionResponse',
28
+ 'ChatQueryRequest',
29
+ 'ChatResponse',
30
+ 'ChatHistoryResponse',
31
+
32
+ # Archive serializers
33
+ 'DocumentArchiveCreateSerializer',
34
+ 'DocumentArchiveSerializer',
35
+ 'DocumentArchiveDetailSerializer',
36
+ 'DocumentArchiveListSerializer',
37
+ 'ArchiveItemSerializer',
38
+ 'ArchiveItemDetailSerializer',
39
+ 'ArchiveItemChunkSerializer',
40
+ 'ArchiveItemChunkDetailSerializer',
41
+ 'ArchiveProcessingResultSerializer',
42
+ 'ArchiveSearchRequestSerializer',
43
+ 'ArchiveSearchResultSerializer',
44
+ 'ArchiveStatisticsSerializer',
45
+ 'VectorizationStatisticsSerializer',
46
+ 'ArchiveUploadSerializer',
47
+ 'ChunkRevectorizationRequestSerializer',
48
+ 'VectorizationResultSerializer',
49
+
50
+ # External data serializers
51
+ 'ExternalDataCreateRequest',
52
+ 'ExternalDataResponse',
53
+ 'ExternalDataListResponse',
54
+ 'ExternalDataChunkResponse',
55
+ 'ExternalDataSearchRequest',
56
+ 'ExternalDataSearchResult',
57
+ 'ExternalDataSearchResponse',
58
+ 'ExternalDataVectorizeRequest',
59
+ 'ExternalDataVectorizeResponse',
60
+ 'ExternalDataStatsResponse',
61
+ 'ExternalDataHealthResponse',
62
+ 'ExternalDataUpdateRequest',
63
+ 'ExternalDataBulkActionRequest',
64
+ 'ExternalDataBulkActionResponse',
65
+ 'ExternalDataQuickAddRequest',
66
+ 'ExternalDataImportRequest',
67
+ 'ExternalDataImportResponse',
68
+ ]