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.
Files changed (246) 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/templates/guide.md +266 -0
  198. django_cfg/modules/django_twilio/twilio_service.py +2 -2
  199. django_cfg/modules/django_unfold/__init__.py +69 -0
  200. django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
  201. django_cfg/modules/django_unfold/dashboard.py +278 -0
  202. django_cfg/modules/django_unfold/icons/README.md +145 -0
  203. django_cfg/modules/django_unfold/icons/__init__.py +12 -0
  204. django_cfg/modules/django_unfold/icons/constants.py +2851 -0
  205. django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
  206. django_cfg/modules/django_unfold/models/__init__.py +42 -0
  207. django_cfg/modules/django_unfold/models/config.py +601 -0
  208. django_cfg/modules/django_unfold/models/dashboard.py +206 -0
  209. django_cfg/modules/django_unfold/models/dropdown.py +40 -0
  210. django_cfg/modules/django_unfold/models/navigation.py +73 -0
  211. django_cfg/modules/django_unfold/models/tabs.py +25 -0
  212. django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
  213. django_cfg/modules/django_unfold/utils.py +140 -0
  214. django_cfg/registry/__init__.py +23 -0
  215. django_cfg/registry/core.py +61 -0
  216. django_cfg/registry/exceptions.py +11 -0
  217. django_cfg/registry/modules.py +12 -0
  218. django_cfg/registry/services.py +26 -0
  219. django_cfg/registry/third_party.py +52 -0
  220. django_cfg/routing/__init__.py +19 -0
  221. django_cfg/routing/callbacks.py +198 -0
  222. django_cfg/routing/routers.py +48 -0
  223. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
  224. django_cfg/templatetags/__init__.py +0 -0
  225. django_cfg/templatetags/django_cfg.py +33 -0
  226. django_cfg/urls.py +33 -0
  227. django_cfg/utils/path_resolution.py +1 -1
  228. django_cfg/utils/smart_defaults.py +7 -61
  229. django_cfg/utils/toolkit.py +663 -0
  230. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
  231. django_cfg-1.2.0.dist-info/RECORD +441 -0
  232. django_cfg/apps/tasks/@docs/README.md +0 -195
  233. django_cfg/archive/django_sample.zip +0 -0
  234. django_cfg/models/unfold.py +0 -271
  235. django_cfg/modules/unfold/__init__.py +0 -29
  236. django_cfg/modules/unfold/dashboard.py +0 -318
  237. django_cfg/pyproject.toml +0 -370
  238. django_cfg/routers.py +0 -83
  239. django_cfg-1.1.81.dist-info/RECORD +0 -278
  240. /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
  241. /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
  242. /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
  243. /django_cfg/{version_check.py → utils/version_check.py} +0 -0
  244. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
  245. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
  246. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,267 @@
1
+ """
2
+ Health Check Views for Django Config Toolkit
3
+
4
+ Provides health check endpoints for monitoring system status.
5
+ """
6
+
7
+ from typing import Dict, Any
8
+ from django.http import JsonResponse
9
+ from django.views import View
10
+ from django.db import connections
11
+ from django.core.cache import cache
12
+ from django.conf import settings
13
+ import time
14
+ import psutil
15
+ import os
16
+ from datetime import datetime
17
+
18
+
19
+ class HealthCheckView(View):
20
+ """
21
+ Health check endpoint that validates system components.
22
+
23
+ GET /health/ returns:
24
+ - Database connectivity
25
+ - Cache availability
26
+ - System resources
27
+ - Configuration status
28
+ """
29
+
30
+ def get(self, request):
31
+ """Return comprehensive health check data."""
32
+ health_data = {
33
+ "status": "healthy",
34
+ "timestamp": datetime.now().isoformat(),
35
+ "checks": {}
36
+ }
37
+
38
+ # Database check
39
+ try:
40
+ db_status = self._check_databases()
41
+ health_data["checks"]["database"] = db_status
42
+ except Exception as e:
43
+ health_data["checks"]["database"] = {
44
+ "status": "error",
45
+ "error": str(e)
46
+ }
47
+ health_data["status"] = "unhealthy"
48
+
49
+ # Cache check
50
+ try:
51
+ cache_status = self._check_cache()
52
+ health_data["checks"]["cache"] = cache_status
53
+ except Exception as e:
54
+ health_data["checks"]["cache"] = {
55
+ "status": "error",
56
+ "error": str(e)
57
+ }
58
+
59
+ # System resources
60
+ try:
61
+ system_status = self._check_system_resources()
62
+ health_data["checks"]["system"] = system_status
63
+ except Exception as e:
64
+ health_data["checks"]["system"] = {
65
+ "status": "warning",
66
+ "error": str(e)
67
+ }
68
+
69
+ # Configuration check
70
+ config_status = self._check_configuration()
71
+ health_data["checks"]["configuration"] = config_status
72
+
73
+ # Overall status
74
+ if any(check.get("status") == "error" for check in health_data["checks"].values()):
75
+ health_data["status"] = "unhealthy"
76
+ elif any(check.get("status") == "warning" for check in health_data["checks"].values()):
77
+ health_data["status"] = "degraded"
78
+
79
+ return JsonResponse(health_data)
80
+
81
+ def _check_databases(self) -> Dict[str, Any]:
82
+ """Check database connectivity."""
83
+ db_status = {
84
+ "status": "healthy",
85
+ "databases": {}
86
+ }
87
+
88
+ for db_name in connections:
89
+ try:
90
+ start_time = time.time()
91
+ connection = connections[db_name]
92
+
93
+ # Test connection
94
+ with connection.cursor() as cursor:
95
+ cursor.execute("SELECT 1")
96
+ cursor.fetchone()
97
+
98
+ response_time = (time.time() - start_time) * 1000
99
+
100
+ db_status["databases"][db_name] = {
101
+ "status": "healthy",
102
+ "response_time_ms": round(response_time, 2),
103
+ "engine": connection.settings_dict.get('ENGINE', 'unknown')
104
+ }
105
+
106
+ except Exception as e:
107
+ db_status["databases"][db_name] = {
108
+ "status": "error",
109
+ "error": str(e)
110
+ }
111
+ db_status["status"] = "error"
112
+
113
+ return db_status
114
+
115
+ def _check_cache(self) -> Dict[str, Any]:
116
+ """Check cache availability."""
117
+ cache_status = {
118
+ "status": "healthy"
119
+ }
120
+
121
+ try:
122
+ # Test cache write/read
123
+ test_key = "health_check"
124
+ test_value = "ok"
125
+
126
+ start_time = time.time()
127
+ cache.set(test_key, test_value, 30)
128
+ retrieved_value = cache.get(test_key)
129
+ response_time = (time.time() - start_time) * 1000
130
+
131
+ if retrieved_value == test_value:
132
+ cache_status.update({
133
+ "status": "healthy",
134
+ "response_time_ms": round(response_time, 2),
135
+ "backend": getattr(settings, 'CACHES', {}).get('default', {}).get('BACKEND', 'unknown')
136
+ })
137
+ else:
138
+ cache_status = {
139
+ "status": "error",
140
+ "error": "Cache read/write test failed"
141
+ }
142
+
143
+ except Exception as e:
144
+ cache_status = {
145
+ "status": "error",
146
+ "error": str(e)
147
+ }
148
+
149
+ return cache_status
150
+
151
+ def _check_system_resources(self) -> Dict[str, Any]:
152
+ """Check system resource usage."""
153
+ try:
154
+ # CPU usage
155
+ cpu_percent = psutil.cpu_percent(interval=1)
156
+
157
+ # Memory usage
158
+ memory = psutil.virtual_memory()
159
+ memory_percent = memory.percent
160
+
161
+ # Disk usage
162
+ disk = psutil.disk_usage('/')
163
+ disk_percent = (disk.used / disk.total) * 100
164
+
165
+ # Load average (Unix-like systems)
166
+ try:
167
+ load_avg = os.getloadavg()
168
+ except (AttributeError, OSError):
169
+ load_avg = None
170
+
171
+ system_status = {
172
+ "status": "healthy",
173
+ "cpu": {
174
+ "usage_percent": cpu_percent,
175
+ "status": "warning" if cpu_percent > 80 else "healthy"
176
+ },
177
+ "memory": {
178
+ "usage_percent": memory_percent,
179
+ "total_gb": round(memory.total / (1024**3), 2),
180
+ "available_gb": round(memory.available / (1024**3), 2),
181
+ "status": "warning" if memory_percent > 80 else "healthy"
182
+ },
183
+ "disk": {
184
+ "usage_percent": round(disk_percent, 2),
185
+ "total_gb": round(disk.total / (1024**3), 2),
186
+ "free_gb": round(disk.free / (1024**3), 2),
187
+ "status": "warning" if disk_percent > 80 else "healthy"
188
+ }
189
+ }
190
+
191
+ if load_avg:
192
+ system_status["load_average"] = {
193
+ "1min": load_avg[0],
194
+ "5min": load_avg[1],
195
+ "15min": load_avg[2]
196
+ }
197
+
198
+ # Overall system status
199
+ if (cpu_percent > 90 or memory_percent > 90 or disk_percent > 90):
200
+ system_status["status"] = "warning"
201
+
202
+ return system_status
203
+
204
+ except Exception as e:
205
+ return {
206
+ "status": "error",
207
+ "error": f"Failed to get system resources: {str(e)}"
208
+ }
209
+
210
+ def _check_configuration(self) -> Dict[str, Any]:
211
+ """Check Django Config Toolkit configuration."""
212
+ try:
213
+ from django_cfg import ConfigToolkit
214
+
215
+ toolkit = ConfigToolkit()
216
+
217
+ config_status = {
218
+ "status": "healthy",
219
+ "toolkit": {
220
+ "version": "1.0.0",
221
+ "init_time_ms": toolkit._init_time_ms,
222
+ "config_count": toolkit._config_count,
223
+ "environment": toolkit.environment,
224
+ "debug": toolkit.debug
225
+ },
226
+ "features": {
227
+ "unfold": toolkit.unfold_enabled,
228
+ "revolution": toolkit.revolution_enabled,
229
+ "constance": toolkit.constance_enabled,
230
+ "logging": toolkit.logging_enabled
231
+ }
232
+ }
233
+
234
+ # Validate configuration
235
+ if not toolkit.secret_key or len(toolkit.secret_key) < 50:
236
+ config_status["status"] = "warning"
237
+ config_status["warnings"] = ["Secret key is too short"]
238
+
239
+ return config_status
240
+
241
+ except Exception as e:
242
+ return {
243
+ "status": "error",
244
+ "error": f"Configuration check failed: {str(e)}"
245
+ }
246
+
247
+
248
+ class SimpleHealthView(View):
249
+ """Simple health check endpoint that just returns OK."""
250
+
251
+ def get(self, request):
252
+ """Return simple OK response."""
253
+ return JsonResponse({
254
+ "status": "ok",
255
+ "timestamp": datetime.now().isoformat()
256
+ })
257
+
258
+
259
+ # URL patterns for health checks
260
+ def get_health_urls():
261
+ """Get URL patterns for health check endpoints."""
262
+ from django.urls import path
263
+
264
+ return [
265
+ path('health/', HealthCheckView.as_view(), name='health-check'),
266
+ path('health/simple/', SimpleHealthView.as_view(), name='simple-health'),
267
+ ]
@@ -37,11 +37,12 @@ from .models import (
37
37
  CacheInfo,
38
38
  LLMError
39
39
  )
40
+ from ...base import BaseCfgModule
40
41
 
41
42
  logger = logging.getLogger(__name__)
42
43
 
43
44
 
44
- class LLMClient:
45
+ class LLMClient(BaseCfgModule):
45
46
  """Universal LLM client with caching and token optimization."""
46
47
 
47
48
  def __init__(
@@ -52,33 +53,40 @@ class LLMClient:
52
53
  cache_ttl: int = 3600,
53
54
  max_cache_size: int = 1000,
54
55
  models_cache_ttl: int = 86400,
55
- config: Optional[Any] = None
56
+ config: Optional[Any] = None,
57
+ preferred_provider: Optional[str] = None
56
58
  ):
57
59
  """
58
60
  Initialize LLM client.
59
61
 
60
62
  Args:
61
- apikey_openrouter: API key for OpenRouter
62
- apikey_openai: API key for OpenAI
63
+ apikey_openrouter: API key for OpenRouter (auto-detected if not provided)
64
+ apikey_openai: API key for OpenAI (auto-detected if not provided)
63
65
  cache_dir: Cache directory path
64
66
  cache_ttl: Cache TTL in seconds
65
67
  max_cache_size: Maximum cache size
66
68
  models_cache_ttl: Models cache TTL in seconds (default: 24 hours)
67
69
  config: DjangoConfig instance for getting headers and settings
70
+ preferred_provider: Preferred provider ("openai" or "openrouter").
71
+ If None, defaults to "openai" for embeddings, "openrouter" for chat
68
72
  """
69
- # Store API keys
73
+ super().__init__()
74
+
75
+ # Auto-detect API keys from config if not provided
76
+ django_config = self.get_config()
77
+ if django_config:
78
+ if apikey_openai is None:
79
+ apikey_openai = getattr(django_config, 'openai_api_key', None)
80
+ # Add other API keys as needed
81
+
82
+ # Store API keys and preferred provider
70
83
  self.apikey_openrouter = apikey_openrouter
71
84
  self.apikey_openai = apikey_openai
85
+ self.preferred_provider = preferred_provider
72
86
 
73
- # Determine primary provider based on available keys
74
- if apikey_openrouter:
75
- self.primary_provider = "openrouter"
76
- self.primary_api_key = apikey_openrouter
77
- elif apikey_openai:
78
- self.primary_provider = "openai"
79
- self.primary_api_key = apikey_openai
80
- else:
81
- raise ValueError("At least one API key (openrouter or openai) must be provided")
87
+ # Determine primary provider based on preference and available keys
88
+ self.primary_provider = self._determine_primary_provider()
89
+ self.primary_api_key = self._get_primary_api_key()
82
90
 
83
91
  self.cache = LLMCache(cache_dir=cache_dir, ttl=cache_ttl, max_size=max_cache_size)
84
92
  self.django_config = config
@@ -161,7 +169,7 @@ class LLMClient:
161
169
 
162
170
  config = base_configs[self.provider].copy()
163
171
 
164
- site_url = getattr(self.django_config, 'site_url', 'https://unrealos.com')
172
+ site_url = getattr(self.django_config, 'site_url', 'https://djangocfg.com')
165
173
  project_name = getattr(self.django_config, 'project_name', 'UnrealOS LLM Client')
166
174
 
167
175
  # Get headers from django config if available
@@ -514,9 +522,12 @@ class LLMClient:
514
522
 
515
523
  start_time = time.time()
516
524
  try:
525
+ # Get the best provider for embedding task
526
+ embedding_provider = self.get_provider_for_task("embedding")
527
+
517
528
  # For OpenRouter, we need to use a different model for embeddings
518
529
  # OpenRouter doesn't support OpenAI embedding models directly
519
- if self.primary_provider == "openrouter":
530
+ if embedding_provider == "openrouter":
520
531
  # Use a text generation model to simulate embeddings
521
532
  # This is a workaround since OpenRouter doesn't have embedding endpoints
522
533
  logger.warning("OpenRouter doesn't support embedding models, using text generation as fallback")
@@ -561,7 +572,8 @@ class LLMClient:
561
572
  )
562
573
  else:
563
574
  # Use real OpenAI embedding API
564
- response = self.client.embeddings.create(
575
+ embedding_client = self.clients[embedding_provider]
576
+ response = embedding_client.embeddings.create(
565
577
  input=text,
566
578
  model=model
567
579
  )
@@ -600,4 +612,54 @@ class LLMClient:
600
612
  error_msg = f"Embedding generation failed: {e}"
601
613
  logger.error(error_msg)
602
614
  raise RuntimeError(error_msg) from e
615
+
616
+ def _determine_primary_provider(self) -> str:
617
+ """
618
+ Determine primary provider based on preference and available keys.
619
+
620
+ Returns:
621
+ Primary provider name
622
+ """
623
+ # If preferred provider is explicitly set and available, use it
624
+ if self.preferred_provider:
625
+ if self.preferred_provider == "openai" and self.apikey_openai:
626
+ return "openai"
627
+ elif self.preferred_provider == "openrouter" and self.apikey_openrouter:
628
+ return "openrouter"
629
+ else:
630
+ logger.warning(f"Preferred provider '{self.preferred_provider}' not available, falling back to auto-detection")
631
+
632
+ # Auto-detection: prefer OpenAI for embeddings, OpenRouter for chat
633
+ if self.apikey_openai:
634
+ return "openai"
635
+ elif self.apikey_openrouter:
636
+ return "openrouter"
637
+ else:
638
+ raise ValueError("At least one API key (openrouter or openai) must be provided")
639
+
640
+ def _get_primary_api_key(self) -> str:
641
+ """Get API key for the primary provider."""
642
+ if self.primary_provider == "openai":
643
+ return self.apikey_openai
644
+ elif self.primary_provider == "openrouter":
645
+ return self.apikey_openrouter
646
+ else:
647
+ raise ValueError(f"Unknown primary provider: {self.primary_provider}")
648
+
649
+ def get_provider_for_task(self, task: str = "chat") -> str:
650
+ """
651
+ Get the best provider for a specific task.
652
+
653
+ Args:
654
+ task: Task type ("chat", "embedding", "completion")
655
+
656
+ Returns:
657
+ Provider name for the task
658
+ """
659
+ # For embeddings, always prefer OpenAI if available
660
+ if task == "embedding" and "openai" in self.clients:
661
+ return "openai"
662
+
663
+ # For other tasks, use primary provider or preferred
664
+ return self.primary_provider
603
665
 
@@ -12,7 +12,7 @@ from typing import Dict, List, Optional, Any, Set
12
12
  from datetime import datetime
13
13
  from pathlib import Path
14
14
 
15
- from django_cfg.modules import BaseModule
15
+ from django_cfg.modules import BaseCfgModule
16
16
  from ..llm.client import LLMClient
17
17
  from ..llm.cache import LLMCache
18
18
  from .cache import TranslationCacheManager
@@ -30,7 +30,7 @@ class LanguageDetectionError(TranslationError):
30
30
  pass
31
31
 
32
32
 
33
- class DjangoTranslator(BaseModule):
33
+ class DjangoTranslator(BaseCfgModule):
34
34
  """
35
35
  Translation Service for django_cfg, configured via DjangoConfig.
36
36
 
@@ -9,10 +9,10 @@ import logging
9
9
  from typing import Optional, Dict, Any, Union
10
10
  from pathlib import Path
11
11
 
12
- from . import BaseModule
12
+ from . import BaseCfgModule
13
13
 
14
14
 
15
- class DjangoLogger(BaseModule):
15
+ class DjangoLogger(BaseCfgModule):
16
16
  """
17
17
  Auto-configuring logger that gets settings from DjangoConfig.
18
18
 
@@ -8,7 +8,7 @@ import os
8
8
  import logging
9
9
  import atexit
10
10
  from typing import Optional
11
- from django_cfg.modules.base import BaseModule
11
+ from django_cfg.modules.base import BaseCfgModule
12
12
  from django_cfg.models.ngrok import NgrokConfig
13
13
 
14
14
  logger = logging.getLogger(__name__)
@@ -100,7 +100,7 @@ class NgrokManager:
100
100
  self.stop_tunnel()
101
101
 
102
102
 
103
- class DjangoNgrok(BaseModule):
103
+ class DjangoNgrok(BaseCfgModule):
104
104
  """Main ngrok service for django-cfg."""
105
105
 
106
106
  def __init__(self):
@@ -9,7 +9,7 @@ from typing import Optional, Dict, Any, List
9
9
  import logging
10
10
  from urllib.parse import urlparse
11
11
 
12
- from django_cfg.modules.base import BaseModule
12
+ from django_cfg.modules.base import BaseCfgModule
13
13
  from django_cfg.models.tasks import TaskConfig, validate_task_config
14
14
  from django_cfg.models.constance import ConstanceField
15
15
 
@@ -35,7 +35,7 @@ except ImportError:
35
35
  logger = logging.getLogger(__name__)
36
36
 
37
37
 
38
- class DjangoTasks(BaseModule):
38
+ class DjangoTasks(BaseCfgModule):
39
39
  """
40
40
  Simplified Django-CFG task service.
41
41
 
@@ -325,7 +325,34 @@ def initialize_task_system():
325
325
 
326
326
  # Django-dramatiq automatically configures the broker from DRAMATIQ_BROKER setting
327
327
  if hasattr(settings, 'DRAMATIQ_BROKER'):
328
- logger.debug("✅ Dramatiq broker configured from Django settings")
328
+ # Configure broker with middleware
329
+ broker_config = settings.DRAMATIQ_BROKER
330
+ middleware_list = getattr(settings, 'DRAMATIQ_MIDDLEWARE', [])
331
+
332
+ # Import and instantiate middleware
333
+ middleware_instances = []
334
+ for middleware_path in middleware_list:
335
+ try:
336
+ module_path, class_name = middleware_path.rsplit('.', 1)
337
+ module = __import__(module_path, fromlist=[class_name])
338
+ middleware_class = getattr(module, class_name)
339
+ middleware_instances.append(middleware_class())
340
+ except Exception as e:
341
+ logger.warning(f"Failed to load middleware {middleware_path}: {e}")
342
+
343
+ # Create broker with middleware
344
+ broker_class_path = broker_config['BROKER']
345
+ module_path, class_name = broker_class_path.rsplit('.', 1)
346
+ module = __import__(module_path, fromlist=[class_name])
347
+ broker_class = getattr(module, class_name)
348
+
349
+ broker_options = broker_config.get('OPTIONS', {})
350
+ broker = broker_class(middleware=middleware_instances, **broker_options)
351
+
352
+ # Set as default broker
353
+ dramatiq.set_broker(broker)
354
+
355
+ logger.debug(f"✅ Dramatiq broker configured with {len(middleware_instances)} middleware")
329
356
  else:
330
357
  logger.warning("DRAMATIQ_BROKER not found in Django settings")
331
358
 
@@ -341,6 +368,43 @@ def initialize_task_system():
341
368
  logger.error(f"Failed to initialize task system: {e}")
342
369
 
343
370
 
371
+ def generate_dramatiq_settings_from_config(config):
372
+ """
373
+ Generate Dramatiq settings from DjangoConfig instance.
374
+
375
+ Args:
376
+ config: DjangoConfig instance with tasks configuration
377
+
378
+ Returns:
379
+ Dict[str, Any]: Dramatiq settings dictionary or empty dict if not enabled
380
+ """
381
+ try:
382
+ if not hasattr(config, "tasks") or not config.tasks or not config.tasks.enabled:
383
+ return {}
384
+
385
+ # Get Redis URL from cache configuration
386
+ redis_url = None
387
+ if config.cache_default and hasattr(config.cache_default, 'redis_url'):
388
+ redis_url = config.cache_default.redis_url
389
+ elif config.cache_default and hasattr(config.cache_default, 'location'):
390
+ redis_url = config.cache_default.location
391
+ else:
392
+ # Fallback to default Redis URL
393
+ redis_url = "redis://localhost:6379"
394
+
395
+ if redis_url:
396
+ dramatiq_settings = config.tasks.get_dramatiq_settings(redis_url)
397
+ logger.debug(f"Generated Dramatiq settings with Redis URL: {redis_url}")
398
+ return dramatiq_settings
399
+ else:
400
+ logger.warning("Tasks enabled but no Redis URL available for Dramatiq")
401
+ return {}
402
+
403
+ except Exception as e:
404
+ logger.error(f"Failed to generate Dramatiq settings: {e}")
405
+ return {}
406
+
407
+
344
408
  def extend_constance_config_with_tasks():
345
409
  """
346
410
  Extend Constance configuration with Dramatiq task fields if tasks are enabled.
@@ -368,6 +432,7 @@ __all__ = [
368
432
  "reset_task_service",
369
433
  "is_task_system_available",
370
434
  "get_task_health",
435
+ "generate_dramatiq_settings_from_config",
371
436
  "extend_constance_config_with_tasks",
372
437
  "initialize_task_system",
373
438
  ]
@@ -9,8 +9,8 @@ import time
9
9
  from typing import Optional, Dict, Any, Union, List, BinaryIO
10
10
  from enum import Enum
11
11
  import yaml
12
- from django_cfg.modules import BaseModule
13
- from django_cfg.exceptions import ConfigurationError
12
+ from django_cfg.modules import BaseCfgModule
13
+ from django_cfg.core.exceptions import ConfigurationError
14
14
  import telebot
15
15
 
16
16
  logger = logging.getLogger(__name__)
@@ -42,7 +42,7 @@ class TelegramSendError(TelegramError):
42
42
  pass
43
43
 
44
44
 
45
- class DjangoTelegram(BaseModule):
45
+ class DjangoTelegram(BaseCfgModule):
46
46
  """
47
47
  Telegram Service for django_cfg, configured via DjangoConfig.
48
48
 
@@ -19,7 +19,7 @@ from asgiref.sync import sync_to_async
19
19
  from django.template.loader import render_to_string
20
20
  from django.utils.html import strip_tags
21
21
 
22
- from django_cfg.modules.base import BaseModule
22
+ from django_cfg.modules.base import BaseCfgModule
23
23
  from django_cfg.modules.django_twilio.models import TwilioConfig, SendGridConfig
24
24
  from django_cfg.modules.django_twilio.exceptions import (
25
25
  TwilioConfigurationError,
@@ -34,7 +34,7 @@ except ImportError:
34
34
  logger = logging.getLogger(__name__)
35
35
 
36
36
 
37
- class SendGridService(BaseModule):
37
+ class SendGridService(BaseCfgModule):
38
38
  """
39
39
  SendGrid service for email delivery.
40
40
 
@@ -27,7 +27,7 @@ from sendgrid.helpers.mail import Mail
27
27
  from asgiref.sync import sync_to_async, async_to_sync
28
28
 
29
29
  # Django CFG imports
30
- from django_cfg.modules.base import BaseModule
30
+ from django_cfg.modules.base import BaseCfgModule
31
31
  from django_cfg.modules.django_twilio.models import (
32
32
  TwilioConfig,
33
33
  TwilioChannelType,
@@ -55,7 +55,7 @@ def is_async_context() -> bool:
55
55
  return False
56
56
 
57
57
 
58
- class BaseTwilioService(BaseModule):
58
+ class BaseTwilioService(BaseCfgModule):
59
59
  """
60
60
  Base service class for all Twilio operations.
61
61
 
@@ -10,7 +10,7 @@ from typing import Optional, Dict, Any
10
10
  from twilio.rest import Client
11
11
  from twilio.base.exceptions import TwilioException
12
12
 
13
- from django_cfg.modules.base import BaseModule
13
+ from django_cfg.modules.base import BaseCfgModule
14
14
  from django_cfg.modules.django_twilio.models import TwilioConfig
15
15
  from django_cfg.modules.django_twilio.exceptions import (
16
16
  TwilioError,
@@ -21,7 +21,7 @@ from django_cfg.modules.django_twilio.exceptions import (
21
21
  logger = logging.getLogger(__name__)
22
22
 
23
23
 
24
- class SimpleTwilioService(BaseModule):
24
+ class SimpleTwilioService(BaseCfgModule):
25
25
  """
26
26
  Simplified Twilio service for basic messaging operations.
27
27