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.
- 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/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.82.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
- django_cfg-1.2.0.dist-info/RECORD +441 -0
- 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.82.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.82.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,319 @@
|
|
1
|
+
"""
|
2
|
+
Django ORM toolset for database operations.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import logging
|
6
|
+
from typing import Dict, Any, List, Optional, Union
|
7
|
+
from pydantic_ai.toolsets import AbstractToolset
|
8
|
+
from pydantic_ai import RunContext
|
9
|
+
from django.db import models
|
10
|
+
from django.apps import apps
|
11
|
+
|
12
|
+
from ..core.dependencies import DjangoDeps
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
class ORMToolset(AbstractToolset[DjangoDeps]):
|
18
|
+
"""
|
19
|
+
Django ORM toolset for safe database operations.
|
20
|
+
|
21
|
+
Provides tools for:
|
22
|
+
- Model queries (read-only by default)
|
23
|
+
- Data aggregation
|
24
|
+
- Relationship traversal
|
25
|
+
- Safe filtering
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self, read_only: bool = True, allowed_models: Optional[List[str]] = None):
|
29
|
+
"""
|
30
|
+
Initialize ORM toolset.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
read_only: If True, only allow read operations
|
34
|
+
allowed_models: List of allowed models in format "app_label.model_name"
|
35
|
+
"""
|
36
|
+
self.read_only = read_only
|
37
|
+
self.allowed_models = set(allowed_models) if allowed_models else None
|
38
|
+
|
39
|
+
@property
|
40
|
+
def id(self) -> str:
|
41
|
+
return "django_orm"
|
42
|
+
|
43
|
+
def _check_model_access(self, app_label: str, model_name: str) -> bool:
|
44
|
+
"""Check if model access is allowed."""
|
45
|
+
if self.allowed_models is None:
|
46
|
+
return True
|
47
|
+
|
48
|
+
model_key = f"{app_label}.{model_name}"
|
49
|
+
return model_key in self.allowed_models
|
50
|
+
|
51
|
+
def _get_model(self, app_label: str, model_name: str) -> models.Model:
|
52
|
+
"""Get Django model class with access check."""
|
53
|
+
if not self._check_model_access(app_label, model_name):
|
54
|
+
raise PermissionError(f"Access to model '{app_label}.{model_name}' not allowed")
|
55
|
+
|
56
|
+
try:
|
57
|
+
return apps.get_model(app_label, model_name)
|
58
|
+
except LookupError:
|
59
|
+
raise ValueError(f"Model '{app_label}.{model_name}' not found")
|
60
|
+
|
61
|
+
async def count_objects(
|
62
|
+
self,
|
63
|
+
ctx: RunContext[DjangoDeps],
|
64
|
+
app_label: str,
|
65
|
+
model_name: str,
|
66
|
+
filters: Optional[Dict[str, Any]] = None
|
67
|
+
) -> int:
|
68
|
+
"""Count objects in model with optional filters."""
|
69
|
+
model = self._get_model(app_label, model_name)
|
70
|
+
|
71
|
+
queryset = model.objects.all()
|
72
|
+
|
73
|
+
if filters:
|
74
|
+
# Apply safe filters
|
75
|
+
safe_filters = self._sanitize_filters(filters)
|
76
|
+
queryset = queryset.filter(**safe_filters)
|
77
|
+
|
78
|
+
return await queryset.acount()
|
79
|
+
|
80
|
+
async def get_object(
|
81
|
+
self,
|
82
|
+
ctx: RunContext[DjangoDeps],
|
83
|
+
app_label: str,
|
84
|
+
model_name: str,
|
85
|
+
object_id: Union[int, str]
|
86
|
+
) -> Optional[Dict[str, Any]]:
|
87
|
+
"""Get single object by ID."""
|
88
|
+
model = self._get_model(app_label, model_name)
|
89
|
+
|
90
|
+
try:
|
91
|
+
obj = await model.objects.aget(pk=object_id)
|
92
|
+
return self._serialize_object(obj)
|
93
|
+
except model.DoesNotExist:
|
94
|
+
return None
|
95
|
+
|
96
|
+
async def list_objects(
|
97
|
+
self,
|
98
|
+
ctx: RunContext[DjangoDeps],
|
99
|
+
app_label: str,
|
100
|
+
model_name: str,
|
101
|
+
filters: Optional[Dict[str, Any]] = None,
|
102
|
+
limit: int = 10,
|
103
|
+
offset: int = 0,
|
104
|
+
order_by: Optional[str] = None
|
105
|
+
) -> Dict[str, Any]:
|
106
|
+
"""List objects with pagination and filtering."""
|
107
|
+
model = self._get_model(app_label, model_name)
|
108
|
+
|
109
|
+
queryset = model.objects.all()
|
110
|
+
|
111
|
+
# Apply filters
|
112
|
+
if filters:
|
113
|
+
safe_filters = self._sanitize_filters(filters)
|
114
|
+
queryset = queryset.filter(**safe_filters)
|
115
|
+
|
116
|
+
# Apply ordering
|
117
|
+
if order_by:
|
118
|
+
# Sanitize order_by field
|
119
|
+
order_field = order_by.lstrip('-')
|
120
|
+
if hasattr(model, order_field):
|
121
|
+
queryset = queryset.order_by(order_by)
|
122
|
+
|
123
|
+
# Get total count
|
124
|
+
total_count = await queryset.acount()
|
125
|
+
|
126
|
+
# Apply pagination
|
127
|
+
queryset = queryset[offset:offset + limit]
|
128
|
+
|
129
|
+
# Serialize objects
|
130
|
+
objects = []
|
131
|
+
async for obj in queryset:
|
132
|
+
objects.append(self._serialize_object(obj))
|
133
|
+
|
134
|
+
return {
|
135
|
+
'objects': objects,
|
136
|
+
'total_count': total_count,
|
137
|
+
'limit': limit,
|
138
|
+
'offset': offset,
|
139
|
+
'has_next': offset + limit < total_count,
|
140
|
+
'has_previous': offset > 0,
|
141
|
+
}
|
142
|
+
|
143
|
+
async def aggregate_data(
|
144
|
+
self,
|
145
|
+
ctx: RunContext[DjangoDeps],
|
146
|
+
app_label: str,
|
147
|
+
model_name: str,
|
148
|
+
aggregations: Dict[str, str],
|
149
|
+
filters: Optional[Dict[str, Any]] = None,
|
150
|
+
group_by: Optional[str] = None
|
151
|
+
) -> Dict[str, Any]:
|
152
|
+
"""Perform aggregations on model data."""
|
153
|
+
from django.db.models import Count, Sum, Avg, Max, Min
|
154
|
+
|
155
|
+
model = self._get_model(app_label, model_name)
|
156
|
+
|
157
|
+
queryset = model.objects.all()
|
158
|
+
|
159
|
+
# Apply filters
|
160
|
+
if filters:
|
161
|
+
safe_filters = self._sanitize_filters(filters)
|
162
|
+
queryset = queryset.filter(**safe_filters)
|
163
|
+
|
164
|
+
# Prepare aggregation functions
|
165
|
+
agg_functions = {
|
166
|
+
'count': Count,
|
167
|
+
'sum': Sum,
|
168
|
+
'avg': Avg,
|
169
|
+
'max': Max,
|
170
|
+
'min': Min,
|
171
|
+
}
|
172
|
+
|
173
|
+
agg_kwargs = {}
|
174
|
+
for alias, agg_spec in aggregations.items():
|
175
|
+
if ':' in agg_spec:
|
176
|
+
func_name, field_name = agg_spec.split(':', 1)
|
177
|
+
else:
|
178
|
+
func_name = agg_spec
|
179
|
+
field_name = 'id' # Default field
|
180
|
+
|
181
|
+
if func_name.lower() in agg_functions:
|
182
|
+
agg_func = agg_functions[func_name.lower()]
|
183
|
+
if func_name.lower() == 'count':
|
184
|
+
agg_kwargs[alias] = agg_func(field_name)
|
185
|
+
else:
|
186
|
+
agg_kwargs[alias] = agg_func(field_name)
|
187
|
+
|
188
|
+
# Perform aggregation
|
189
|
+
if group_by and hasattr(model, group_by):
|
190
|
+
# Group by field
|
191
|
+
result = []
|
192
|
+
async for item in queryset.values(group_by).annotate(**agg_kwargs):
|
193
|
+
result.append(item)
|
194
|
+
return {'grouped_results': result}
|
195
|
+
else:
|
196
|
+
# Simple aggregation
|
197
|
+
result = await queryset.aaggregate(**agg_kwargs)
|
198
|
+
return result
|
199
|
+
|
200
|
+
async def search_objects(
|
201
|
+
self,
|
202
|
+
ctx: RunContext[DjangoDeps],
|
203
|
+
app_label: str,
|
204
|
+
model_name: str,
|
205
|
+
search_fields: List[str],
|
206
|
+
query: str,
|
207
|
+
limit: int = 10
|
208
|
+
) -> List[Dict[str, Any]]:
|
209
|
+
"""Search objects using icontains on specified fields."""
|
210
|
+
from django.db.models import Q
|
211
|
+
|
212
|
+
model = self._get_model(app_label, model_name)
|
213
|
+
|
214
|
+
# Build search query
|
215
|
+
search_q = Q()
|
216
|
+
for field in search_fields:
|
217
|
+
if hasattr(model, field):
|
218
|
+
search_q |= Q(**{f"{field}__icontains": query})
|
219
|
+
|
220
|
+
if not search_q:
|
221
|
+
return []
|
222
|
+
|
223
|
+
# Execute search
|
224
|
+
queryset = model.objects.filter(search_q)[:limit]
|
225
|
+
|
226
|
+
results = []
|
227
|
+
async for obj in queryset:
|
228
|
+
results.append(self._serialize_object(obj))
|
229
|
+
|
230
|
+
return results
|
231
|
+
|
232
|
+
async def get_related_objects(
|
233
|
+
self,
|
234
|
+
ctx: RunContext[DjangoDeps],
|
235
|
+
app_label: str,
|
236
|
+
model_name: str,
|
237
|
+
object_id: Union[int, str],
|
238
|
+
relation_name: str,
|
239
|
+
limit: int = 10
|
240
|
+
) -> List[Dict[str, Any]]:
|
241
|
+
"""Get related objects through foreign key or many-to-many."""
|
242
|
+
model = self._get_model(app_label, model_name)
|
243
|
+
|
244
|
+
try:
|
245
|
+
obj = await model.objects.aget(pk=object_id)
|
246
|
+
except model.DoesNotExist:
|
247
|
+
return []
|
248
|
+
|
249
|
+
# Check if relation exists
|
250
|
+
if not hasattr(obj, relation_name):
|
251
|
+
return []
|
252
|
+
|
253
|
+
relation = getattr(obj, relation_name)
|
254
|
+
|
255
|
+
# Handle different relation types
|
256
|
+
if hasattr(relation, 'all'):
|
257
|
+
# Many-to-many or reverse foreign key
|
258
|
+
related_objects = relation.all()[:limit]
|
259
|
+
else:
|
260
|
+
# Single related object
|
261
|
+
if relation:
|
262
|
+
return [self._serialize_object(relation)]
|
263
|
+
else:
|
264
|
+
return []
|
265
|
+
|
266
|
+
results = []
|
267
|
+
async for related_obj in related_objects:
|
268
|
+
results.append(self._serialize_object(related_obj))
|
269
|
+
|
270
|
+
return results
|
271
|
+
|
272
|
+
def _sanitize_filters(self, filters: Dict[str, Any]) -> Dict[str, Any]:
|
273
|
+
"""Sanitize filters to prevent dangerous operations."""
|
274
|
+
safe_filters = {}
|
275
|
+
|
276
|
+
# Allowed lookup types
|
277
|
+
allowed_lookups = {
|
278
|
+
'exact', 'iexact', 'contains', 'icontains', 'startswith', 'istartswith',
|
279
|
+
'endswith', 'iendswith', 'gt', 'gte', 'lt', 'lte', 'in', 'isnull',
|
280
|
+
'date', 'year', 'month', 'day', 'week_day', 'hour', 'minute', 'second'
|
281
|
+
}
|
282
|
+
|
283
|
+
for key, value in filters.items():
|
284
|
+
# Check for dangerous operations
|
285
|
+
if '__' in key:
|
286
|
+
field, lookup = key.rsplit('__', 1)
|
287
|
+
if lookup in allowed_lookups:
|
288
|
+
safe_filters[key] = value
|
289
|
+
else:
|
290
|
+
# Direct field lookup (exact)
|
291
|
+
safe_filters[key] = value
|
292
|
+
|
293
|
+
return safe_filters
|
294
|
+
|
295
|
+
def _serialize_object(self, obj: models.Model) -> Dict[str, Any]:
|
296
|
+
"""Serialize Django model object to dictionary."""
|
297
|
+
data = {}
|
298
|
+
|
299
|
+
for field in obj._meta.fields:
|
300
|
+
value = getattr(obj, field.name)
|
301
|
+
|
302
|
+
# Handle special field types
|
303
|
+
if hasattr(value, 'isoformat'):
|
304
|
+
# DateTime fields
|
305
|
+
data[field.name] = value.isoformat()
|
306
|
+
elif hasattr(value, '__dict__') and hasattr(value, 'pk'):
|
307
|
+
# Related objects - just include ID and str representation
|
308
|
+
data[field.name] = {
|
309
|
+
'id': value.pk,
|
310
|
+
'str': str(value)
|
311
|
+
}
|
312
|
+
else:
|
313
|
+
data[field.name] = value
|
314
|
+
|
315
|
+
# Add primary key and string representation
|
316
|
+
data['pk'] = obj.pk
|
317
|
+
data['str'] = str(obj)
|
318
|
+
|
319
|
+
return data
|
@@ -0,0 +1,46 @@
|
|
1
|
+
"""
|
2
|
+
Django Orchestrator URLs.
|
3
|
+
|
4
|
+
Provides API endpoints for agent management and execution.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from django.urls import path, include
|
8
|
+
from rest_framework.routers import DefaultRouter
|
9
|
+
|
10
|
+
# Import views when they're created
|
11
|
+
# from .api.views import AgentViewSet, WorkflowViewSet, ExecutionViewSet
|
12
|
+
|
13
|
+
app_name = 'django_orchestrator'
|
14
|
+
|
15
|
+
# Create router for API endpoints
|
16
|
+
router = DefaultRouter()
|
17
|
+
|
18
|
+
# Register viewsets when they're created
|
19
|
+
# router.register(r'agents', AgentViewSet, basename='agent')
|
20
|
+
# router.register(r'workflows', WorkflowViewSet, basename='workflow')
|
21
|
+
# router.register(r'executions', ExecutionViewSet, basename='execution')
|
22
|
+
|
23
|
+
urlpatterns = [
|
24
|
+
# API endpoints
|
25
|
+
path('api/', include(router.urls)),
|
26
|
+
|
27
|
+
# Health check endpoint
|
28
|
+
path('health/', lambda request: JsonResponse({'status': 'ok', 'service': 'django-orchestrator'}), name='health'),
|
29
|
+
]
|
30
|
+
|
31
|
+
# For now, create a simple health endpoint
|
32
|
+
from django.http import JsonResponse
|
33
|
+
|
34
|
+
def health_check(request):
|
35
|
+
"""Health check endpoint for Django Orchestrator."""
|
36
|
+
return JsonResponse({
|
37
|
+
'status': 'ok',
|
38
|
+
'service': 'django-orchestrator',
|
39
|
+
'version': '0.1.0'
|
40
|
+
})
|
41
|
+
|
42
|
+
# Update urlpatterns with the actual function
|
43
|
+
urlpatterns = [
|
44
|
+
path('api/', include(router.urls)),
|
45
|
+
path('health/', health_check, name='health'),
|
46
|
+
]
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# Django CFG Knowledge Base
|
2
|
+
|
3
|
+
Enterprise-grade RAG (Retrieval-Augmented Generation) application built on Django with pgvector semantic search, Dramatiq background processing, and comprehensive API endpoints for document management and AI-powered chat.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- 📄 **Document Management**: Upload, process, and manage documents with automatic chunking
|
8
|
+
- 🔍 **Semantic Search**: pgvector-powered cosine similarity search
|
9
|
+
- 🤖 **AI Chat**: RAG-powered conversational interface with context retrieval
|
10
|
+
- 👥 **Multi-tenant**: Complete user isolation and access control
|
11
|
+
- ⚡ **Background Processing**: Async document processing with Dramatiq
|
12
|
+
- 💰 **Cost Tracking**: Monitor LLM usage and costs
|
13
|
+
- 🎨 **Admin Interface**: Beautiful Unfold-styled admin with statistics
|
14
|
+
- 🔒 **Type Safety**: Pydantic v2 validation throughout
|
15
|
+
- ✅ **Full Testing**: Comprehensive test coverage
|
16
|
+
|
17
|
+
## Quick Start
|
18
|
+
|
19
|
+
### 1. Enable in Configuration
|
20
|
+
|
21
|
+
```python
|
22
|
+
from django_cfg import DjangoConfig
|
23
|
+
|
24
|
+
class MyConfig(DjangoConfig):
|
25
|
+
enable_knowbase: bool = True
|
26
|
+
|
27
|
+
# Required for AI features
|
28
|
+
openai_api_key: str = "${OPENAI_API_KEY}"
|
29
|
+
|
30
|
+
# Optional: Configure similarity thresholds
|
31
|
+
knowbase_document_threshold: float = 0.7
|
32
|
+
knowbase_archive_threshold: float = 0.6
|
33
|
+
```
|
34
|
+
|
35
|
+
### 2. Add to INSTALLED_APPS
|
36
|
+
|
37
|
+
```python
|
38
|
+
INSTALLED_APPS = [
|
39
|
+
# ... other apps
|
40
|
+
'django_cfg.apps.knowbase',
|
41
|
+
]
|
42
|
+
```
|
43
|
+
|
44
|
+
### 3. Run Migrations
|
45
|
+
|
46
|
+
```bash
|
47
|
+
python manage.py migrate
|
48
|
+
```
|
49
|
+
|
50
|
+
### 4. Start Using
|
51
|
+
|
52
|
+
The Knowledge Base will be available at `/cfg/knowbase/` with:
|
53
|
+
|
54
|
+
- **Admin Interface**: Document and chat management
|
55
|
+
- **API Endpoints**: RESTful API for integration
|
56
|
+
- **Chat Interface**: AI-powered conversational search
|
57
|
+
|
58
|
+
## Architecture
|
59
|
+
|
60
|
+
### Models
|
61
|
+
- **Document**: File storage and metadata
|
62
|
+
- **DocumentChunk**: Text chunks with embeddings
|
63
|
+
- **ChatSession**: Conversation management
|
64
|
+
- **ChatMessage**: Individual messages with context
|
65
|
+
|
66
|
+
### Services
|
67
|
+
- **DocumentService**: Document processing and management
|
68
|
+
- **ChatService**: AI chat with RAG capabilities
|
69
|
+
- **SearchService**: Semantic search across all content types
|
70
|
+
|
71
|
+
### Background Tasks
|
72
|
+
- Document processing and vectorization
|
73
|
+
- Embedding generation and optimization
|
74
|
+
- Maintenance and cleanup tasks
|
75
|
+
|
76
|
+
## Configuration Options
|
77
|
+
|
78
|
+
```python
|
79
|
+
class MyConfig(DjangoConfig):
|
80
|
+
# Enable/disable the app
|
81
|
+
enable_knowbase: bool = True
|
82
|
+
|
83
|
+
# AI Configuration
|
84
|
+
openai_api_key: str = "${OPENAI_API_KEY}"
|
85
|
+
|
86
|
+
# Search Thresholds
|
87
|
+
knowbase_document_threshold: float = 0.7 # Document similarity
|
88
|
+
knowbase_archive_threshold: float = 0.6 # Code similarity
|
89
|
+
|
90
|
+
# Processing Settings
|
91
|
+
knowbase_chunk_size: int = 1000 # Text chunk size
|
92
|
+
knowbase_overlap_size: int = 200 # Chunk overlap
|
93
|
+
knowbase_batch_size: int = 50 # Embedding batch size
|
94
|
+
```
|
95
|
+
|
96
|
+
## API Usage
|
97
|
+
|
98
|
+
### Document Upload
|
99
|
+
|
100
|
+
```python
|
101
|
+
import requests
|
102
|
+
|
103
|
+
response = requests.post('/cfg/knowbase/api/documents/', {
|
104
|
+
'title': 'My Document',
|
105
|
+
'file': open('document.pdf', 'rb')
|
106
|
+
})
|
107
|
+
```
|
108
|
+
|
109
|
+
### Chat Query
|
110
|
+
|
111
|
+
```python
|
112
|
+
response = requests.post('/cfg/knowbase/api/chat/sessions/{session_id}/query/', {
|
113
|
+
'query': 'What is machine learning?',
|
114
|
+
'max_tokens': 500
|
115
|
+
})
|
116
|
+
```
|
117
|
+
|
118
|
+
### Search Documents
|
119
|
+
|
120
|
+
```python
|
121
|
+
response = requests.get('/cfg/knowbase/api/search/', {
|
122
|
+
'query': 'artificial intelligence',
|
123
|
+
'limit': 10
|
124
|
+
})
|
125
|
+
```
|
126
|
+
|
127
|
+
## Integration with External Apps
|
128
|
+
|
129
|
+
The Knowledge Base supports integration with external Django apps through the `ExternalDataMixin`:
|
130
|
+
|
131
|
+
```python
|
132
|
+
from django_cfg.apps.knowbase.mixins import ExternalDataMixin
|
133
|
+
|
134
|
+
class MyModel(ExternalDataMixin, models.Model):
|
135
|
+
name = models.CharField(max_length=100)
|
136
|
+
description = models.TextField()
|
137
|
+
|
138
|
+
# That's it! Auto AI integration enabled
|
139
|
+
```
|
140
|
+
|
141
|
+
## Requirements
|
142
|
+
|
143
|
+
- **PostgreSQL** with pgvector extension
|
144
|
+
- **Redis** for Dramatiq task queue
|
145
|
+
- **OpenAI API Key** for embeddings and chat
|
146
|
+
- **Python 3.11+** and **Django 5.0+**
|
147
|
+
|
148
|
+
## License
|
149
|
+
|
150
|
+
Part of Django CFG - MIT License
|
@@ -0,0 +1,27 @@
|
|
1
|
+
"""
|
2
|
+
Django SaaS Knowledge Assistant
|
3
|
+
|
4
|
+
Enterprise-grade RAG (Retrieval-Augmented Generation) application built on Django 5.2
|
5
|
+
with pgvector semantic search, Dramatiq background processing, and comprehensive
|
6
|
+
API endpoints for document management and AI-powered chat.
|
7
|
+
|
8
|
+
Key Features:
|
9
|
+
- Document ingestion with automatic chunking and embedding generation
|
10
|
+
- Semantic search using pgvector cosine similarity
|
11
|
+
- RAG-powered chat with context retrieval
|
12
|
+
- Multi-tenant user isolation
|
13
|
+
- Background processing with Dramatiq
|
14
|
+
- Cost tracking for LLM usage monitoring
|
15
|
+
- Comprehensive admin interface with Unfold styling
|
16
|
+
- Type-safe APIs with Pydantic v2 validation
|
17
|
+
- Full test coverage
|
18
|
+
|
19
|
+
Architecture:
|
20
|
+
- Models: Document, DocumentChunk, ChatSession, ChatMessage
|
21
|
+
- Services: DocumentService, ChatService, SearchService
|
22
|
+
- Tasks: Async document processing, maintenance, optimization
|
23
|
+
- APIs: REST endpoints with DRF and OpenAPI documentation
|
24
|
+
- Admin: Unfold-optimized interfaces with statistics
|
25
|
+
"""
|
26
|
+
|
27
|
+
default_app_config = 'django_cfg.apps.knowbase.apps.KnowbaseConfig'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"""
|
2
|
+
Knowledge Base Admin Configuration
|
3
|
+
|
4
|
+
Unfold-optimized admin interfaces for knowledge management.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from .document_admin import *
|
8
|
+
from .chat_admin import *
|
9
|
+
from .archive_admin import *
|
10
|
+
from .external_data_admin import *
|
11
|
+
|
12
|
+
__all__ = [
|
13
|
+
'DocumentCategoryAdmin',
|
14
|
+
'DocumentAdmin',
|
15
|
+
'DocumentChunkAdmin',
|
16
|
+
'DocumentArchiveAdmin',
|
17
|
+
'ArchiveItemAdmin',
|
18
|
+
'ArchiveItemChunkAdmin',
|
19
|
+
'ExternalDataAdmin',
|
20
|
+
'ExternalDataChunkAdmin',
|
21
|
+
'ChatSessionAdmin',
|
22
|
+
'ChatMessageAdmin',
|
23
|
+
]
|