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,386 @@
1
+ """
2
+ Archive serializers for DRF API.
3
+ """
4
+
5
+ from rest_framework import serializers
6
+ from typing import Dict, Any, List
7
+ from ..models.archive import DocumentArchive, ArchiveItem, ArchiveItemChunk, ArchiveType, ContentType, ChunkType
8
+ from ..models.document import DocumentCategory
9
+
10
+
11
+ class DocumentArchiveCreateSerializer(serializers.Serializer):
12
+ """Document archive creation request serializer."""
13
+
14
+ archive_file = serializers.FileField(
15
+ help_text="Archive file to upload"
16
+ )
17
+ title = serializers.CharField(
18
+ max_length=512,
19
+ required=False,
20
+ help_text="Archive title (auto-generated from filename if not provided)"
21
+ )
22
+ description = serializers.CharField(
23
+ max_length=2000,
24
+ required=False,
25
+ allow_blank=True,
26
+ help_text="Archive description"
27
+ )
28
+ category_ids = serializers.ListField(
29
+ child=serializers.UUIDField(),
30
+ required=False,
31
+ default=list,
32
+ help_text="List of category IDs"
33
+ )
34
+ is_public = serializers.BooleanField(
35
+ default=True,
36
+ help_text="Whether archive is publicly accessible"
37
+ )
38
+ process_immediately = serializers.BooleanField(
39
+ default=True,
40
+ help_text="Process archive synchronously"
41
+ )
42
+
43
+ def validate_archive_file(self, value):
44
+ """Validate uploaded archive file."""
45
+ if not value:
46
+ raise serializers.ValidationError('Archive file is required')
47
+
48
+ # Check file size (max 100MB)
49
+ max_size = 100 * 1024 * 1024 # 100MB
50
+ if value.size > max_size:
51
+ raise serializers.ValidationError(
52
+ f'File too large. Maximum size is {max_size // (1024*1024)}MB'
53
+ )
54
+
55
+ # Check file extension
56
+ allowed_extensions = [
57
+ '.zip', '.jar', '.war', '.ear', # ZIP formats
58
+ '.tar.gz', '.tgz', # TAR.GZ formats
59
+ '.tar.bz2', '.tbz2', '.tar.bzip2', # TAR.BZ2 formats
60
+ '.tar' # TAR formats
61
+ ]
62
+ filename = value.name.lower()
63
+ if not any(filename.endswith(ext) for ext in allowed_extensions):
64
+ raise serializers.ValidationError(
65
+ f'Unsupported file format. Allowed: {", ".join(allowed_extensions)}'
66
+ )
67
+
68
+ return value
69
+
70
+ def validate_title(self, value: str) -> str:
71
+ """Validate title format."""
72
+ if value and not value.strip():
73
+ raise serializers.ValidationError('Title cannot be empty')
74
+ return value.strip() if value else value
75
+
76
+ def validate_category_ids(self, value: List[str]) -> List[str]:
77
+ """Validate category IDs exist."""
78
+ if value:
79
+ existing_ids = set(
80
+ DocumentCategory.objects.filter(
81
+ id__in=value
82
+ ).values_list('id', flat=True)
83
+ )
84
+
85
+ invalid_ids = set(str(id) for id in value) - set(str(id) for id in existing_ids)
86
+ if invalid_ids:
87
+ raise serializers.ValidationError(
88
+ f'Invalid category IDs: {", ".join(invalid_ids)}'
89
+ )
90
+
91
+ return value
92
+
93
+
94
+ class DocumentCategorySerializer(serializers.ModelSerializer):
95
+ """Document category serializer."""
96
+
97
+ class Meta:
98
+ model = DocumentCategory
99
+ fields = ['id', 'name', 'description', 'is_public', 'created_at']
100
+ read_only_fields = ['id', 'created_at']
101
+
102
+
103
+ class ArchiveItemSerializer(serializers.ModelSerializer):
104
+ """Archive item serializer."""
105
+
106
+ class Meta:
107
+ model = ArchiveItem
108
+ fields = [
109
+ 'id', 'relative_path', 'item_name', 'item_type', 'content_type',
110
+ 'file_size', 'is_processable', 'language', 'encoding',
111
+ 'chunks_count', 'total_tokens', 'processing_cost',
112
+ 'created_at', 'updated_at'
113
+ ]
114
+ read_only_fields = [
115
+ 'id', 'content_type', 'is_processable', 'language', 'encoding',
116
+ 'chunks_count', 'total_tokens', 'processing_cost',
117
+ 'created_at', 'updated_at'
118
+ ]
119
+
120
+
121
+ class ArchiveItemDetailSerializer(ArchiveItemSerializer):
122
+ """Detailed archive item serializer with content."""
123
+
124
+ raw_content = serializers.CharField(read_only=True)
125
+ metadata = serializers.JSONField(read_only=True)
126
+
127
+ class Meta(ArchiveItemSerializer.Meta):
128
+ fields = ArchiveItemSerializer.Meta.fields + ['raw_content', 'metadata']
129
+
130
+
131
+ class ArchiveItemChunkSerializer(serializers.ModelSerializer):
132
+ """Archive item chunk serializer."""
133
+
134
+ context_summary = serializers.SerializerMethodField()
135
+
136
+ class Meta:
137
+ model = ArchiveItemChunk
138
+ fields = [
139
+ 'id', 'content', 'chunk_index', 'chunk_type',
140
+ 'token_count', 'character_count', 'embedding_model',
141
+ 'embedding_cost', 'context_summary', 'created_at'
142
+ ]
143
+ read_only_fields = [
144
+ 'id', 'token_count', 'character_count', 'embedding_model',
145
+ 'embedding_cost', 'context_summary', 'created_at'
146
+ ]
147
+
148
+ def get_context_summary(self, obj: ArchiveItemChunk) -> Dict[str, Any]:
149
+ """Get context summary for display."""
150
+ return obj.get_context_summary()
151
+
152
+
153
+ class ArchiveItemChunkDetailSerializer(ArchiveItemChunkSerializer):
154
+ """Detailed chunk serializer with full context."""
155
+
156
+ context_metadata = serializers.JSONField(read_only=True)
157
+
158
+ class Meta(ArchiveItemChunkSerializer.Meta):
159
+ fields = ArchiveItemChunkSerializer.Meta.fields + ['context_metadata']
160
+
161
+
162
+ class DocumentArchiveSerializer(serializers.ModelSerializer):
163
+ """Document archive serializer."""
164
+
165
+ categories = DocumentCategorySerializer(many=True, read_only=True)
166
+ processing_progress = serializers.ReadOnlyField()
167
+ vectorization_progress = serializers.ReadOnlyField()
168
+ is_processed = serializers.ReadOnlyField()
169
+
170
+ class Meta:
171
+ model = DocumentArchive
172
+ fields = [
173
+ 'id', 'title', 'description', 'categories', 'is_public',
174
+ 'archive_file', 'original_filename', 'file_size', 'archive_type',
175
+ 'processing_status', 'processed_at', 'processing_duration_ms',
176
+ 'processing_error', 'total_items', 'processed_items',
177
+ 'total_chunks', 'vectorized_chunks', 'total_tokens',
178
+ 'total_cost_usd', 'processing_progress', 'vectorization_progress',
179
+ 'is_processed', 'created_at', 'updated_at'
180
+ ]
181
+ read_only_fields = [
182
+ 'id', 'original_filename', 'file_size', 'archive_type',
183
+ 'processing_status', 'processed_at', 'processing_duration_ms',
184
+ 'processing_error', 'total_items', 'processed_items',
185
+ 'total_chunks', 'vectorized_chunks', 'total_tokens',
186
+ 'total_cost_usd', 'processing_progress', 'vectorization_progress',
187
+ 'is_processed', 'created_at', 'updated_at'
188
+ ]
189
+
190
+
191
+ class DocumentArchiveDetailSerializer(DocumentArchiveSerializer):
192
+ """Detailed archive serializer with items."""
193
+
194
+ items = ArchiveItemSerializer(many=True, read_only=True)
195
+ file_tree = serializers.SerializerMethodField()
196
+
197
+ class Meta(DocumentArchiveSerializer.Meta):
198
+ fields = DocumentArchiveSerializer.Meta.fields + ['items', 'file_tree', 'metadata']
199
+
200
+ def get_file_tree(self, obj: DocumentArchive) -> Dict[str, Any]:
201
+ """Get hierarchical file tree."""
202
+ return obj.get_file_tree()
203
+
204
+
205
+ class DocumentArchiveListSerializer(serializers.ModelSerializer):
206
+ """Simplified archive serializer for list views."""
207
+
208
+ categories = DocumentCategorySerializer(many=True, read_only=True)
209
+ processing_progress = serializers.ReadOnlyField()
210
+
211
+ class Meta:
212
+ model = DocumentArchive
213
+ fields = [
214
+ 'id', 'title', 'description', 'categories', 'is_public',
215
+ 'original_filename', 'file_size', 'archive_type',
216
+ 'processing_status', 'processed_at', 'total_items',
217
+ 'total_chunks', 'total_cost_usd', 'processing_progress',
218
+ 'created_at'
219
+ ]
220
+ read_only_fields = fields
221
+
222
+
223
+ class ArchiveProcessingResultSerializer(serializers.Serializer):
224
+ """Archive processing result serializer."""
225
+
226
+ archive_id = serializers.UUIDField(read_only=True)
227
+ status = serializers.CharField(read_only=True)
228
+ processing_time_ms = serializers.IntegerField(read_only=True)
229
+ items_processed = serializers.IntegerField(read_only=True)
230
+ chunks_created = serializers.IntegerField(read_only=True)
231
+ vectorized_chunks = serializers.IntegerField(read_only=True)
232
+ total_cost_usd = serializers.FloatField(read_only=True)
233
+ error_message = serializers.CharField(read_only=True, required=False)
234
+
235
+
236
+ class ArchiveSearchRequestSerializer(serializers.Serializer):
237
+ """Archive search request serializer."""
238
+
239
+ query = serializers.CharField(
240
+ min_length=1,
241
+ max_length=500,
242
+ help_text="Search query"
243
+ )
244
+ content_types = serializers.MultipleChoiceField(
245
+ choices=ContentType.choices,
246
+ required=False,
247
+ help_text="Filter by content types"
248
+ )
249
+ languages = serializers.ListField(
250
+ child=serializers.CharField(max_length=50),
251
+ required=False,
252
+ help_text="Filter by programming languages"
253
+ )
254
+ chunk_types = serializers.MultipleChoiceField(
255
+ choices=ChunkType.choices,
256
+ required=False,
257
+ help_text="Filter by chunk types"
258
+ )
259
+ archive_ids = serializers.ListField(
260
+ child=serializers.UUIDField(),
261
+ required=False,
262
+ help_text="Search within specific archives"
263
+ )
264
+ limit = serializers.IntegerField(
265
+ min_value=1,
266
+ max_value=50,
267
+ default=10,
268
+ help_text="Maximum number of results"
269
+ )
270
+ similarity_threshold = serializers.FloatField(
271
+ min_value=0.0,
272
+ max_value=1.0,
273
+ default=0.7,
274
+ help_text="Minimum similarity threshold"
275
+ )
276
+
277
+
278
+ class ArchiveSearchResultSerializer(serializers.Serializer):
279
+ """Archive search result serializer."""
280
+
281
+ chunk = ArchiveItemChunkSerializer(read_only=True)
282
+ similarity_score = serializers.FloatField(read_only=True)
283
+ context_summary = serializers.DictField(read_only=True)
284
+ archive_info = serializers.DictField(read_only=True)
285
+ item_info = serializers.DictField(read_only=True)
286
+
287
+
288
+ class ArchiveStatisticsSerializer(serializers.Serializer):
289
+ """Archive statistics serializer."""
290
+
291
+ total_archives = serializers.IntegerField(read_only=True)
292
+ processed_archives = serializers.IntegerField(read_only=True)
293
+ failed_archives = serializers.IntegerField(read_only=True)
294
+ total_items = serializers.IntegerField(read_only=True)
295
+ total_chunks = serializers.IntegerField(read_only=True)
296
+ total_tokens = serializers.IntegerField(read_only=True)
297
+ total_cost = serializers.FloatField(read_only=True)
298
+ avg_processing_time = serializers.FloatField(read_only=True)
299
+ avg_items_per_archive = serializers.FloatField(read_only=True)
300
+ avg_chunks_per_archive = serializers.FloatField(read_only=True)
301
+
302
+
303
+ class VectorizationStatisticsSerializer(serializers.Serializer):
304
+ """Vectorization statistics serializer."""
305
+
306
+ total_chunks = serializers.IntegerField(read_only=True)
307
+ vectorized_chunks = serializers.IntegerField(read_only=True)
308
+ pending_chunks = serializers.IntegerField(read_only=True)
309
+ vectorization_rate = serializers.FloatField(read_only=True)
310
+ total_tokens = serializers.IntegerField(read_only=True)
311
+ total_cost = serializers.FloatField(read_only=True)
312
+ avg_tokens_per_chunk = serializers.FloatField(read_only=True)
313
+ avg_cost_per_chunk = serializers.FloatField(read_only=True)
314
+
315
+
316
+ class ContentTypeDistributionSerializer(serializers.Serializer):
317
+ """Content type distribution serializer."""
318
+
319
+ content_type = serializers.CharField(read_only=True)
320
+ count = serializers.IntegerField(read_only=True)
321
+ percentage = serializers.FloatField(read_only=True)
322
+
323
+
324
+ class LanguageDistributionSerializer(serializers.Serializer):
325
+ """Language distribution serializer."""
326
+
327
+ language = serializers.CharField(read_only=True)
328
+ count = serializers.IntegerField(read_only=True)
329
+ percentage = serializers.FloatField(read_only=True)
330
+
331
+
332
+ class ArchiveUploadSerializer(serializers.Serializer):
333
+ """Archive file upload serializer."""
334
+
335
+ file = serializers.FileField(
336
+ help_text="Archive file to upload"
337
+ )
338
+
339
+ def validate_file(self, value):
340
+ """Validate uploaded file."""
341
+
342
+ # Check file size (200MB limit)
343
+ max_size = 200 * 1024 * 1024
344
+ if value.size > max_size:
345
+ raise serializers.ValidationError(
346
+ f'File too large. Maximum size is {max_size // (1024*1024)}MB'
347
+ )
348
+
349
+ # Check file extension
350
+ allowed_extensions = ['.zip', '.tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2']
351
+ filename = value.name.lower()
352
+
353
+ if not any(filename.endswith(ext) for ext in allowed_extensions):
354
+ raise serializers.ValidationError(
355
+ f'Unsupported file type. Allowed: {", ".join(allowed_extensions)}'
356
+ )
357
+
358
+ return value
359
+
360
+
361
+ class ChunkRevectorizationRequestSerializer(serializers.Serializer):
362
+ """Chunk re-vectorization request serializer."""
363
+
364
+ chunk_ids = serializers.ListField(
365
+ child=serializers.UUIDField(),
366
+ min_length=1,
367
+ help_text="List of chunk IDs to re-vectorize"
368
+ )
369
+ force = serializers.BooleanField(
370
+ default=False,
371
+ help_text="Force re-vectorization even if already vectorized"
372
+ )
373
+
374
+
375
+ class VectorizationResultSerializer(serializers.Serializer):
376
+ """Vectorization result serializer."""
377
+
378
+ vectorized_count = serializers.IntegerField(read_only=True)
379
+ failed_count = serializers.IntegerField(read_only=True)
380
+ total_tokens = serializers.IntegerField(read_only=True)
381
+ total_cost = serializers.FloatField(read_only=True)
382
+ success_rate = serializers.FloatField(read_only=True)
383
+ errors = serializers.ListField(
384
+ child=serializers.CharField(),
385
+ read_only=True
386
+ )
@@ -0,0 +1,137 @@
1
+ """
2
+ Chat serializers for DRF API.
3
+ """
4
+
5
+ from rest_framework import serializers
6
+ from ..models import ChatSession, ChatMessage
7
+ from ..utils.validation import is_valid_float, safe_float
8
+
9
+
10
+ class ChatSessionCreateSerializer(serializers.Serializer):
11
+ """Chat session creation request serializer."""
12
+
13
+ title = serializers.CharField(
14
+ max_length=255,
15
+ default="",
16
+ allow_blank=True,
17
+ help_text="Session title"
18
+ )
19
+ model_name = serializers.CharField(
20
+ max_length=100,
21
+ default="openai/gpt-4o-mini",
22
+ help_text="LLM model to use"
23
+ )
24
+ temperature = serializers.FloatField(
25
+ min_value=0.0,
26
+ max_value=2.0,
27
+ default=0.7,
28
+ help_text="Response creativity"
29
+ )
30
+ max_context_chunks = serializers.IntegerField(
31
+ min_value=1,
32
+ max_value=10,
33
+ default=5,
34
+ help_text="Maximum context chunks"
35
+ )
36
+
37
+
38
+ class ChatQuerySerializer(serializers.Serializer):
39
+ """Chat query request serializer."""
40
+
41
+ session_id = serializers.UUIDField(
42
+ required=False,
43
+ allow_null=True,
44
+ help_text="Chat session ID (creates new if not provided)"
45
+ )
46
+ query = serializers.CharField(
47
+ min_length=1,
48
+ max_length=2000,
49
+ help_text="User query"
50
+ )
51
+ max_tokens = serializers.IntegerField(
52
+ min_value=1,
53
+ max_value=4000,
54
+ default=1000,
55
+ help_text="Maximum response tokens"
56
+ )
57
+ include_sources = serializers.BooleanField(
58
+ default=True,
59
+ help_text="Include source documents in response"
60
+ )
61
+
62
+ def validate_query(self, value):
63
+ """Validate query content."""
64
+ if not value.strip():
65
+ raise serializers.ValidationError('Query cannot be empty')
66
+ return value.strip()
67
+
68
+
69
+ class ChatSourceSerializer(serializers.Serializer):
70
+ """Chat source document information serializer."""
71
+
72
+ document_title = serializers.CharField()
73
+ chunk_content = serializers.CharField()
74
+ similarity = serializers.FloatField()
75
+
76
+ def validate_similarity(self, value):
77
+ """Validate similarity value to prevent NaN in JSON."""
78
+ if not is_valid_float(value):
79
+ raise serializers.ValidationError('Invalid similarity value')
80
+ return value
81
+
82
+
83
+ class ChatResponseSerializer(serializers.Serializer):
84
+ """Chat response serializer."""
85
+
86
+ message_id = serializers.UUIDField()
87
+ content = serializers.CharField()
88
+ tokens_used = serializers.IntegerField()
89
+ cost_usd = serializers.FloatField()
90
+ processing_time_ms = serializers.IntegerField()
91
+ model_used = serializers.CharField()
92
+ sources = ChatSourceSerializer(many=True, required=False, allow_null=True)
93
+
94
+
95
+ class ChatSessionSerializer(serializers.ModelSerializer):
96
+ """Chat session response serializer."""
97
+
98
+ id = serializers.UUIDField(read_only=True)
99
+ total_cost_usd = serializers.FloatField(read_only=True)
100
+
101
+ def validate_total_cost_usd(self, value):
102
+ """Validate cost value to prevent NaN in JSON."""
103
+ return safe_float(value, 0.0)
104
+
105
+ class Meta:
106
+ model = ChatSession
107
+ fields = [
108
+ 'id', 'title', 'is_active', 'messages_count', 'total_tokens_used',
109
+ 'total_cost_usd', 'model_name', 'temperature', 'max_context_chunks',
110
+ 'created_at', 'updated_at'
111
+ ]
112
+
113
+
114
+ class ChatMessageSerializer(serializers.ModelSerializer):
115
+ """Chat message response serializer."""
116
+
117
+ id = serializers.UUIDField(read_only=True)
118
+ cost_usd = serializers.FloatField(read_only=True)
119
+
120
+ def validate_cost_usd(self, value):
121
+ """Validate cost value to prevent NaN in JSON."""
122
+ return safe_float(value, 0.0)
123
+
124
+ class Meta:
125
+ model = ChatMessage
126
+ fields = [
127
+ 'id', 'role', 'content', 'tokens_used', 'cost_usd',
128
+ 'processing_time_ms', 'created_at', 'context_chunks'
129
+ ]
130
+
131
+
132
+ class ChatHistorySerializer(serializers.Serializer):
133
+ """Chat history response serializer."""
134
+
135
+ session_id = serializers.UUIDField()
136
+ messages = ChatMessageSerializer(many=True)
137
+ total_messages = serializers.IntegerField()
@@ -0,0 +1,94 @@
1
+ """
2
+ Document serializers for DRF API.
3
+ """
4
+
5
+ from rest_framework import serializers
6
+ from typing import Dict, Any
7
+ from ..models import Document, ProcessingStatus
8
+
9
+
10
+ class DocumentCreateSerializer(serializers.Serializer):
11
+ """Document creation request serializer."""
12
+
13
+ title = serializers.CharField(
14
+ max_length=512,
15
+ help_text="Document title"
16
+ )
17
+ content = serializers.CharField(
18
+ min_length=10,
19
+ max_length=1_000_000,
20
+ help_text="Document content"
21
+ )
22
+ file_type = serializers.RegexField(
23
+ regex=r"^[a-z]+/[a-z0-9\-\+\.]+$",
24
+ default="text/plain",
25
+ help_text="MIME type"
26
+ )
27
+ metadata = serializers.JSONField(
28
+ default=dict,
29
+ help_text="Additional metadata"
30
+ )
31
+
32
+ def validate_content(self, value):
33
+ """Validate content for security."""
34
+ dangerous_patterns = [
35
+ '<script', 'javascript:', 'data:',
36
+ 'vbscript:', 'onload=', 'onerror='
37
+ ]
38
+
39
+ if any(pattern in value.lower() for pattern in dangerous_patterns):
40
+ raise serializers.ValidationError('Content contains potentially unsafe elements')
41
+
42
+ return value
43
+
44
+ def validate_title(self, value):
45
+ """Validate title format."""
46
+ if not value.strip():
47
+ raise serializers.ValidationError('Title cannot be empty')
48
+ return value.strip()
49
+
50
+
51
+ class DocumentSerializer(serializers.ModelSerializer):
52
+ """Document response serializer."""
53
+
54
+ id = serializers.UUIDField(read_only=True)
55
+ processing_status = serializers.CharField(read_only=True)
56
+ chunks_count = serializers.IntegerField(read_only=True)
57
+ total_tokens = serializers.IntegerField(read_only=True)
58
+ total_cost_usd = serializers.FloatField(read_only=True)
59
+ created_at = serializers.DateTimeField(read_only=True)
60
+ updated_at = serializers.DateTimeField(read_only=True)
61
+ processing_started_at = serializers.DateTimeField(read_only=True)
62
+ processing_completed_at = serializers.DateTimeField(read_only=True)
63
+ processing_error = serializers.CharField(read_only=True)
64
+
65
+ class Meta:
66
+ model = Document
67
+ fields = [
68
+ 'id', 'title', 'file_type', 'file_size', 'processing_status',
69
+ 'chunks_count', 'total_tokens', 'total_cost_usd', 'created_at',
70
+ 'updated_at', 'processing_started_at', 'processing_completed_at',
71
+ 'processing_error', 'metadata'
72
+ ]
73
+
74
+
75
+ class DocumentStatsSerializer(serializers.Serializer):
76
+ """Document processing statistics serializer."""
77
+
78
+ total_documents = serializers.IntegerField()
79
+ completed_documents = serializers.IntegerField()
80
+ processing_success_rate = serializers.FloatField()
81
+ total_chunks = serializers.IntegerField()
82
+ total_tokens = serializers.IntegerField()
83
+ total_cost_usd = serializers.FloatField()
84
+ avg_processing_time_seconds = serializers.FloatField()
85
+
86
+
87
+ class DocumentProcessingStatusSerializer(serializers.Serializer):
88
+ """Document processing status serializer."""
89
+
90
+ id = serializers.UUIDField()
91
+ status = serializers.CharField()
92
+ progress = serializers.JSONField()
93
+ error = serializers.CharField(allow_null=True, required=False)
94
+ processing_time_seconds = serializers.FloatField(allow_null=True, required=False)