django-cfg 1.1.81__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.
- django_cfg/__init__.py +20 -448
- django_cfg/apps/accounts/README.md +3 -3
- django_cfg/apps/accounts/admin/__init__.py +0 -2
- django_cfg/apps/accounts/admin/activity.py +2 -9
- django_cfg/apps/accounts/admin/filters.py +0 -42
- django_cfg/apps/accounts/admin/inlines.py +8 -8
- django_cfg/apps/accounts/admin/otp.py +5 -5
- django_cfg/apps/accounts/admin/registration_source.py +1 -8
- django_cfg/apps/accounts/admin/user.py +12 -20
- django_cfg/apps/accounts/managers/user_manager.py +2 -129
- django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
- django_cfg/apps/accounts/models.py +3 -123
- django_cfg/apps/accounts/serializers/otp.py +40 -44
- django_cfg/apps/accounts/serializers/profile.py +0 -2
- django_cfg/apps/accounts/services/otp_service.py +98 -186
- django_cfg/apps/accounts/signals.py +25 -15
- django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
- django_cfg/apps/accounts/views/otp.py +35 -36
- django_cfg/apps/agents/README.md +129 -0
- django_cfg/apps/agents/__init__.py +68 -0
- django_cfg/apps/agents/admin/__init__.py +17 -0
- django_cfg/apps/agents/admin/execution_admin.py +460 -0
- django_cfg/apps/agents/admin/registry_admin.py +360 -0
- django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
- django_cfg/apps/agents/apps.py +29 -0
- django_cfg/apps/agents/core/__init__.py +20 -0
- django_cfg/apps/agents/core/agent.py +281 -0
- django_cfg/apps/agents/core/dependencies.py +154 -0
- django_cfg/apps/agents/core/exceptions.py +66 -0
- django_cfg/apps/agents/core/models.py +106 -0
- django_cfg/apps/agents/core/orchestrator.py +391 -0
- django_cfg/apps/agents/examples/__init__.py +3 -0
- django_cfg/apps/agents/examples/simple_example.py +161 -0
- django_cfg/apps/agents/integration/__init__.py +14 -0
- django_cfg/apps/agents/integration/middleware.py +80 -0
- django_cfg/apps/agents/integration/registry.py +345 -0
- django_cfg/apps/agents/integration/signals.py +50 -0
- django_cfg/apps/agents/management/__init__.py +3 -0
- django_cfg/apps/agents/management/commands/__init__.py +3 -0
- django_cfg/apps/agents/management/commands/create_agent.py +365 -0
- django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
- django_cfg/apps/agents/managers/__init__.py +23 -0
- django_cfg/apps/agents/managers/execution.py +236 -0
- django_cfg/apps/agents/managers/registry.py +254 -0
- django_cfg/apps/agents/managers/toolsets.py +496 -0
- django_cfg/apps/agents/migrations/0001_initial.py +286 -0
- django_cfg/apps/agents/migrations/__init__.py +5 -0
- django_cfg/apps/agents/models/__init__.py +15 -0
- django_cfg/apps/agents/models/execution.py +215 -0
- django_cfg/apps/agents/models/registry.py +220 -0
- django_cfg/apps/agents/models/toolsets.py +305 -0
- django_cfg/apps/agents/patterns/__init__.py +24 -0
- django_cfg/apps/agents/patterns/content_agents.py +234 -0
- django_cfg/apps/agents/toolsets/__init__.py +15 -0
- django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
- django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
- django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
- django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
- django_cfg/apps/agents/urls.py +46 -0
- django_cfg/apps/knowbase/README.md +150 -0
- django_cfg/apps/knowbase/__init__.py +27 -0
- django_cfg/apps/knowbase/admin/__init__.py +23 -0
- django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
- django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
- django_cfg/apps/knowbase/admin/document_admin.py +650 -0
- django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
- django_cfg/apps/knowbase/apps.py +81 -0
- django_cfg/apps/knowbase/config/README.md +176 -0
- django_cfg/apps/knowbase/config/__init__.py +51 -0
- django_cfg/apps/knowbase/config/constance_fields.py +186 -0
- django_cfg/apps/knowbase/config/constance_settings.py +200 -0
- django_cfg/apps/knowbase/config/settings.py +444 -0
- django_cfg/apps/knowbase/examples/__init__.py +3 -0
- django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
- django_cfg/apps/knowbase/management/__init__.py +0 -0
- django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
- django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
- django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
- django_cfg/apps/knowbase/managers/__init__.py +22 -0
- django_cfg/apps/knowbase/managers/archive.py +426 -0
- django_cfg/apps/knowbase/managers/base.py +32 -0
- django_cfg/apps/knowbase/managers/chat.py +141 -0
- django_cfg/apps/knowbase/managers/document.py +203 -0
- django_cfg/apps/knowbase/managers/external_data.py +471 -0
- django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
- django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
- django_cfg/apps/knowbase/migrations/__init__.py +5 -0
- django_cfg/apps/knowbase/mixins/__init__.py +15 -0
- django_cfg/apps/knowbase/mixins/config.py +108 -0
- django_cfg/apps/knowbase/mixins/creator.py +81 -0
- django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
- django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
- django_cfg/apps/knowbase/mixins/service.py +362 -0
- django_cfg/apps/knowbase/models/__init__.py +41 -0
- django_cfg/apps/knowbase/models/archive.py +599 -0
- django_cfg/apps/knowbase/models/base.py +58 -0
- django_cfg/apps/knowbase/models/chat.py +157 -0
- django_cfg/apps/knowbase/models/document.py +267 -0
- django_cfg/apps/knowbase/models/external_data.py +376 -0
- django_cfg/apps/knowbase/serializers/__init__.py +68 -0
- django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
- django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
- django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
- django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
- django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
- django_cfg/apps/knowbase/services/__init__.py +40 -0
- django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
- django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
- django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
- django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
- django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
- django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
- django_cfg/apps/knowbase/services/base.py +53 -0
- django_cfg/apps/knowbase/services/chat_service.py +239 -0
- django_cfg/apps/knowbase/services/document_service.py +144 -0
- django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
- django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
- django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
- django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
- django_cfg/apps/knowbase/services/embedding/models.py +229 -0
- django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
- django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
- django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
- django_cfg/apps/knowbase/services/search_service.py +293 -0
- django_cfg/apps/knowbase/signals/__init__.py +21 -0
- django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
- django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
- django_cfg/apps/knowbase/signals/document_signals.py +143 -0
- django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
- django_cfg/apps/knowbase/tasks/__init__.py +39 -0
- django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
- django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
- django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
- django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
- django_cfg/apps/knowbase/urls.py +43 -0
- django_cfg/apps/knowbase/utils/__init__.py +12 -0
- django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
- django_cfg/apps/knowbase/utils/text_processing.py +375 -0
- django_cfg/apps/knowbase/utils/validation.py +99 -0
- django_cfg/apps/knowbase/views/__init__.py +28 -0
- django_cfg/apps/knowbase/views/archive_views.py +469 -0
- django_cfg/apps/knowbase/views/base.py +49 -0
- django_cfg/apps/knowbase/views/chat_views.py +181 -0
- django_cfg/apps/knowbase/views/document_views.py +183 -0
- django_cfg/apps/knowbase/views/public_views.py +129 -0
- django_cfg/apps/leads/admin.py +70 -0
- django_cfg/apps/newsletter/admin.py +234 -0
- django_cfg/apps/newsletter/admin_filters.py +124 -0
- django_cfg/apps/support/admin.py +196 -0
- django_cfg/apps/support/admin_filters.py +71 -0
- django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
- django_cfg/apps/urls.py +5 -4
- django_cfg/cli/README.md +1 -1
- django_cfg/cli/commands/create_project.py +2 -2
- django_cfg/cli/commands/info.py +1 -1
- django_cfg/config.py +44 -0
- django_cfg/core/config.py +29 -82
- django_cfg/core/environment.py +1 -1
- django_cfg/core/generation.py +19 -107
- django_cfg/{integration.py → core/integration.py} +18 -16
- django_cfg/core/validation.py +1 -1
- django_cfg/management/__init__.py +1 -1
- django_cfg/management/commands/__init__.py +1 -1
- django_cfg/management/commands/auto_generate.py +482 -0
- django_cfg/management/commands/migrator.py +19 -101
- django_cfg/management/commands/test_email.py +1 -1
- django_cfg/middleware/README.md +0 -158
- django_cfg/middleware/__init__.py +0 -2
- django_cfg/middleware/user_activity.py +3 -3
- django_cfg/models/api.py +145 -0
- django_cfg/models/base.py +287 -0
- django_cfg/models/cache.py +4 -4
- django_cfg/models/constance.py +25 -88
- django_cfg/models/database.py +9 -9
- django_cfg/models/drf.py +3 -36
- django_cfg/models/email.py +163 -0
- django_cfg/models/environment.py +276 -0
- django_cfg/models/limits.py +1 -1
- django_cfg/models/logging.py +366 -0
- django_cfg/models/revolution.py +41 -2
- django_cfg/models/security.py +125 -0
- django_cfg/models/services.py +1 -1
- django_cfg/modules/__init__.py +2 -56
- django_cfg/modules/base.py +78 -52
- django_cfg/modules/django_currency/service.py +2 -2
- django_cfg/modules/django_email.py +2 -2
- django_cfg/modules/django_health.py +267 -0
- django_cfg/modules/django_llm/llm/client.py +79 -17
- django_cfg/modules/django_llm/translator/translator.py +2 -2
- django_cfg/modules/django_logger.py +2 -2
- django_cfg/modules/django_ngrok.py +2 -2
- django_cfg/modules/django_tasks.py +68 -3
- django_cfg/modules/django_telegram.py +3 -3
- django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
- django_cfg/modules/django_twilio/service.py +2 -2
- django_cfg/modules/django_twilio/simple_service.py +2 -2
- django_cfg/modules/django_twilio/templates/guide.md +266 -0
- django_cfg/modules/django_twilio/twilio_service.py +2 -2
- django_cfg/modules/django_unfold/__init__.py +69 -0
- django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
- django_cfg/modules/django_unfold/dashboard.py +278 -0
- django_cfg/modules/django_unfold/icons/README.md +145 -0
- django_cfg/modules/django_unfold/icons/__init__.py +12 -0
- django_cfg/modules/django_unfold/icons/constants.py +2851 -0
- django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
- django_cfg/modules/django_unfold/models/__init__.py +42 -0
- django_cfg/modules/django_unfold/models/config.py +601 -0
- django_cfg/modules/django_unfold/models/dashboard.py +206 -0
- django_cfg/modules/django_unfold/models/dropdown.py +40 -0
- django_cfg/modules/django_unfold/models/navigation.py +73 -0
- django_cfg/modules/django_unfold/models/tabs.py +25 -0
- django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
- django_cfg/modules/django_unfold/utils.py +140 -0
- django_cfg/registry/__init__.py +23 -0
- django_cfg/registry/core.py +61 -0
- django_cfg/registry/exceptions.py +11 -0
- django_cfg/registry/modules.py +12 -0
- django_cfg/registry/services.py +26 -0
- django_cfg/registry/third_party.py +52 -0
- django_cfg/routing/__init__.py +19 -0
- django_cfg/routing/callbacks.py +198 -0
- django_cfg/routing/routers.py +48 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
- django_cfg/templatetags/__init__.py +0 -0
- django_cfg/templatetags/django_cfg.py +33 -0
- django_cfg/urls.py +33 -0
- django_cfg/utils/path_resolution.py +1 -1
- django_cfg/utils/smart_defaults.py +7 -61
- django_cfg/utils/toolkit.py +663 -0
- {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
- django_cfg-1.2.0.dist-info/RECORD +441 -0
- django_cfg/apps/tasks/@docs/README.md +0 -195
- django_cfg/archive/django_sample.zip +0 -0
- django_cfg/models/unfold.py +0 -271
- django_cfg/modules/unfold/__init__.py +0 -29
- django_cfg/modules/unfold/dashboard.py +0 -318
- django_cfg/pyproject.toml +0 -370
- django_cfg/routers.py +0 -83
- django_cfg-1.1.81.dist-info/RECORD +0 -278
- /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
- /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
- /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
- /django_cfg/{version_check.py → utils/version_check.py} +0 -0
- {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
- {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.1.81.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)
|