django-cfg 1.3.13__py3-none-any.whl → 1.4.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 (438) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/user_admin.py +39 -16
  3. django_cfg/apps/accounts/serializers/profile.py +1 -1
  4. django_cfg/apps/accounts/services/otp_service.py +18 -11
  5. django_cfg/apps/accounts/signals.py +15 -24
  6. django_cfg/apps/accounts/utils/notifications.py +217 -358
  7. django_cfg/apps/accounts/views/otp.py +2 -2
  8. django_cfg/apps/accounts/views/webhook.py +1 -1
  9. django_cfg/apps/agents/core/django_agent.py +1 -1
  10. django_cfg/apps/api/commands/views.py +66 -83
  11. django_cfg/apps/api/health/drf_views.py +269 -0
  12. django_cfg/apps/api/health/serializers.py +45 -0
  13. django_cfg/apps/api/health/urls.py +6 -1
  14. django_cfg/apps/knowbase/admin/actions/__init__.py +13 -0
  15. django_cfg/apps/knowbase/admin/actions/visibility_actions.py +56 -0
  16. django_cfg/apps/knowbase/admin/document_admin.py +136 -270
  17. django_cfg/apps/knowbase/admin/helpers/__init__.py +17 -0
  18. django_cfg/apps/knowbase/admin/helpers/configs.py +72 -0
  19. django_cfg/apps/knowbase/admin/helpers/display_helpers.py +156 -0
  20. django_cfg/apps/knowbase/admin/helpers/statistics.py +108 -0
  21. django_cfg/apps/knowbase/config/constance_fields.py +1 -1
  22. django_cfg/apps/knowbase/config/settings.py +2 -2
  23. django_cfg/apps/knowbase/mixins/__init__.py +19 -2
  24. django_cfg/apps/knowbase/mixins/config/__init__.py +14 -0
  25. django_cfg/apps/knowbase/mixins/config/defaults.py +75 -0
  26. django_cfg/apps/knowbase/mixins/config/meta_config.py +120 -0
  27. django_cfg/apps/knowbase/mixins/creator.py +10 -10
  28. django_cfg/apps/knowbase/mixins/external_data_mixin.py +105 -403
  29. django_cfg/apps/knowbase/mixins/generators/__init__.py +16 -0
  30. django_cfg/apps/knowbase/mixins/generators/content_generator.py +218 -0
  31. django_cfg/apps/knowbase/mixins/generators/field_analyzer.py +76 -0
  32. django_cfg/apps/knowbase/mixins/generators/metadata_generator.py +124 -0
  33. django_cfg/apps/knowbase/mixins/service.py +2 -2
  34. django_cfg/apps/knowbase/services/archive/__init__.py +1 -0
  35. django_cfg/apps/knowbase/services/archive/analyzers/__init__.py +17 -0
  36. django_cfg/apps/knowbase/services/archive/analyzers/complexity_analyzer.py +33 -0
  37. django_cfg/apps/knowbase/services/archive/analyzers/purpose_detector.py +36 -0
  38. django_cfg/apps/knowbase/services/archive/analyzers/quality_analyzer.py +39 -0
  39. django_cfg/apps/knowbase/services/archive/analyzers/tag_generator.py +103 -0
  40. django_cfg/apps/knowbase/services/archive/chunking/__init__.py +19 -0
  41. django_cfg/apps/knowbase/services/archive/chunking/base.py +81 -0
  42. django_cfg/apps/knowbase/services/archive/chunking/json_chunker.py +62 -0
  43. django_cfg/apps/knowbase/services/archive/chunking/markdown_chunker.py +107 -0
  44. django_cfg/apps/knowbase/services/archive/chunking/python_chunker.py +248 -0
  45. django_cfg/apps/knowbase/services/archive/chunking/text_chunker.py +70 -0
  46. django_cfg/apps/knowbase/services/archive/chunking_service.py +110 -729
  47. django_cfg/apps/knowbase/services/archive/context/__init__.py +14 -0
  48. django_cfg/apps/knowbase/services/archive/context/builders.py +220 -0
  49. django_cfg/apps/knowbase/services/archive/context/models.py +38 -0
  50. django_cfg/apps/knowbase/services/embedding/models.py +18 -14
  51. django_cfg/apps/knowbase/services/embedding/processors.py +6 -3
  52. django_cfg/apps/knowbase/tasks/document_processing.py +11 -3
  53. django_cfg/apps/leads/tests.py +1 -1
  54. django_cfg/apps/payments/admin/api_keys_admin.py +1 -1
  55. django_cfg/apps/payments/admin/balance_admin.py +1 -1
  56. django_cfg/apps/payments/admin/currencies_admin.py +1 -1
  57. django_cfg/apps/payments/admin/payments_admin.py +1 -1
  58. django_cfg/apps/payments/admin/subscriptions_admin.py +1 -1
  59. django_cfg/apps/payments/admin_interface/templates/payments/base.html +59 -126
  60. django_cfg/apps/payments/admin_interface/views/api/payments.py +1 -1
  61. django_cfg/apps/payments/admin_interface/views/api/stats.py +1 -1
  62. django_cfg/apps/payments/admin_interface/views/api/users.py +1 -1
  63. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +1 -1
  64. django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +1 -1
  65. django_cfg/apps/payments/admin_interface/views/base.py +29 -2
  66. django_cfg/apps/payments/apps.py +1 -1
  67. django_cfg/apps/payments/config/django_cfg_integration.py +2 -2
  68. django_cfg/apps/payments/config/helpers.py +3 -2
  69. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +1 -1
  70. django_cfg/apps/payments/management/commands/currency_stats.py +1 -1
  71. django_cfg/apps/payments/management/commands/manage_currencies.py +1 -1
  72. django_cfg/apps/payments/management/commands/manage_providers.py +1 -1
  73. django_cfg/apps/payments/management/commands/process_pending_payments.py +1 -1
  74. django_cfg/apps/payments/management/commands/test_providers.py +1 -1
  75. django_cfg/apps/payments/middleware/api_access.py +1 -1
  76. django_cfg/apps/payments/middleware/rate_limiting.py +1 -1
  77. django_cfg/apps/payments/middleware/usage_tracking.py +1 -1
  78. django_cfg/apps/payments/models/balance.py +2 -2
  79. django_cfg/apps/payments/models/managers/api_key_managers.py +1 -1
  80. django_cfg/apps/payments/models/managers/balance_managers.py +1 -1
  81. django_cfg/apps/payments/models/managers/currency_managers.py +1 -1
  82. django_cfg/apps/payments/models/managers/payment_managers.py +1 -1
  83. django_cfg/apps/payments/models/managers/subscription_managers.py +1 -1
  84. django_cfg/apps/payments/models/payments.py +2 -2
  85. django_cfg/apps/payments/services/cache_service/__init__.py +1 -1
  86. django_cfg/apps/payments/services/cache_service/simple_cache.py +10 -5
  87. django_cfg/apps/payments/services/core/base.py +1 -1
  88. django_cfg/apps/payments/services/core/currency/__init__.py +13 -0
  89. django_cfg/apps/payments/services/core/currency/currency_converter.py +57 -0
  90. django_cfg/apps/payments/services/core/currency/currency_validator.py +61 -0
  91. django_cfg/apps/payments/services/core/operations/__init__.py +15 -0
  92. django_cfg/apps/payments/services/core/operations/payment_canceller.py +100 -0
  93. django_cfg/apps/payments/services/core/operations/payment_creator.py +196 -0
  94. django_cfg/apps/payments/services/core/operations/status_checker.py +100 -0
  95. django_cfg/apps/payments/services/core/payment_service.py +124 -612
  96. django_cfg/apps/payments/services/core/providers/__init__.py +13 -0
  97. django_cfg/apps/payments/services/core/providers/provider_client.py +132 -0
  98. django_cfg/apps/payments/services/core/providers/status_mapper.py +89 -0
  99. django_cfg/apps/payments/services/core/utils/__init__.py +13 -0
  100. django_cfg/apps/payments/services/core/utils/data_converter.py +48 -0
  101. django_cfg/apps/payments/services/core/utils/statistics_calculator.py +69 -0
  102. django_cfg/apps/payments/services/providers/base.py +1 -1
  103. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +3 -3
  104. django_cfg/apps/payments/services/providers/nowpayments/parsers/__init__.py +9 -0
  105. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/__init__.py +23 -0
  106. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/constants.py +23 -0
  107. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/currency_names.py +244 -0
  108. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/patterns.py +511 -0
  109. django_cfg/apps/payments/services/providers/nowpayments/parsers/parser.py +168 -0
  110. django_cfg/apps/payments/services/providers/nowpayments/provider.py +1 -1
  111. django_cfg/apps/payments/services/providers/nowpayments/sync.py +1 -1
  112. django_cfg/apps/payments/services/providers/registry.py +1 -1
  113. django_cfg/apps/payments/services/providers/sync_service.py +1 -1
  114. django_cfg/apps/payments/signals/__init__.py +1 -1
  115. django_cfg/apps/payments/signals/api_key_signals.py +1 -1
  116. django_cfg/apps/payments/signals/balance_signals.py +1 -1
  117. django_cfg/apps/payments/signals/payment_signals.py +1 -1
  118. django_cfg/apps/payments/signals/subscription_signals.py +1 -1
  119. django_cfg/apps/payments/views/api/api_keys.py +1 -1
  120. django_cfg/apps/payments/views/api/balances.py +1 -1
  121. django_cfg/apps/payments/views/api/base.py +1 -1
  122. django_cfg/apps/payments/views/api/currencies.py +1 -1
  123. django_cfg/apps/payments/views/api/payments.py +1 -1
  124. django_cfg/apps/payments/views/api/subscriptions.py +1 -1
  125. django_cfg/apps/payments/views/api/webhooks.py +1 -1
  126. django_cfg/apps/payments/views/serializers/api_keys.py +1 -1
  127. django_cfg/apps/payments/views/serializers/balances.py +1 -1
  128. django_cfg/apps/payments/views/serializers/currencies.py +1 -1
  129. django_cfg/apps/payments/views/serializers/payments.py +1 -1
  130. django_cfg/apps/payments/views/serializers/subscriptions.py +1 -1
  131. django_cfg/apps/payments/views/serializers/webhooks.py +1 -1
  132. django_cfg/apps/support/admin/support_admin.py +21 -13
  133. django_cfg/apps/support/templates/support/chat/access_denied.html +21 -27
  134. django_cfg/apps/support/templates/support/chat/ticket_chat.html +183 -254
  135. django_cfg/apps/support/utils/support_email_service.py +1 -1
  136. django_cfg/apps/tasks/templates/tasks/layout/base.html +20 -115
  137. django_cfg/apps/tasks/utils/simulator.py +1 -1
  138. django_cfg/apps/tasks/views/dashboard.py +33 -3
  139. django_cfg/apps/urls.py +5 -1
  140. django_cfg/cli/README.md +57 -471
  141. django_cfg/cli/commands/create_project.py +140 -529
  142. django_cfg/cli/main.py +13 -10
  143. django_cfg/core/__init__.py +63 -6
  144. django_cfg/core/base/__init__.py +5 -0
  145. django_cfg/core/base/config_model.py +652 -0
  146. django_cfg/core/builders/__init__.py +11 -0
  147. django_cfg/core/builders/apps_builder.py +258 -0
  148. django_cfg/core/builders/middleware_builder.py +115 -0
  149. django_cfg/core/builders/security_builder.py +96 -0
  150. django_cfg/core/config.py +20 -892
  151. django_cfg/core/constants.py +69 -0
  152. django_cfg/core/environment/__init__.py +9 -0
  153. django_cfg/core/exceptions.py +45 -298
  154. django_cfg/core/generation/__init__.py +51 -0
  155. django_cfg/core/generation/core_generators/__init__.py +0 -0
  156. django_cfg/core/generation/core_generators/settings.py +90 -0
  157. django_cfg/core/generation/core_generators/static.py +82 -0
  158. django_cfg/core/generation/core_generators/templates.py +141 -0
  159. django_cfg/core/generation/data_generators/__init__.py +15 -0
  160. django_cfg/core/generation/data_generators/cache.py +132 -0
  161. django_cfg/core/generation/data_generators/database.py +117 -0
  162. django_cfg/core/generation/generation.py +92 -0
  163. django_cfg/core/generation/integration_generators/__init__.py +21 -0
  164. django_cfg/core/generation/integration_generators/api.py +237 -0
  165. django_cfg/core/generation/integration_generators/sessions.py +65 -0
  166. django_cfg/core/generation/integration_generators/tailwind.py +54 -0
  167. django_cfg/core/generation/integration_generators/tasks.py +92 -0
  168. django_cfg/core/generation/integration_generators/third_party.py +144 -0
  169. django_cfg/core/generation/orchestrator.py +285 -0
  170. django_cfg/core/generation/protocols.py +30 -0
  171. django_cfg/core/generation/security_generators/__init__.py +0 -0
  172. django_cfg/core/generation/utility_generators/__init__.py +24 -0
  173. django_cfg/core/generation/utility_generators/email.py +58 -0
  174. django_cfg/core/generation/utility_generators/i18n.py +66 -0
  175. django_cfg/core/generation/utility_generators/limits.py +58 -0
  176. django_cfg/core/generation/utility_generators/logging.py +66 -0
  177. django_cfg/core/generation/utility_generators/security.py +101 -0
  178. django_cfg/core/generation/utils/__init__.py +0 -0
  179. django_cfg/core/generation/utils/helpers.py +32 -0
  180. django_cfg/core/integration/__init__.py +18 -25
  181. django_cfg/core/integration/display/startup.py +146 -133
  182. django_cfg/core/integration/url_integration.py +13 -2
  183. django_cfg/core/services/__init__.py +5 -0
  184. django_cfg/core/services/config_service.py +121 -0
  185. django_cfg/core/state/__init__.py +9 -0
  186. django_cfg/core/state/registry.py +84 -0
  187. django_cfg/core/types/__init__.py +15 -0
  188. django_cfg/core/types/aliases.py +15 -0
  189. django_cfg/core/types/enums.py +49 -0
  190. django_cfg/dashboard/DEBUG_README.md +105 -0
  191. django_cfg/dashboard/REFACTORING_SUMMARY.md +237 -0
  192. django_cfg/dashboard/__init__.py +24 -0
  193. django_cfg/dashboard/components.py +308 -0
  194. django_cfg/dashboard/debug.py +176 -0
  195. django_cfg/dashboard/management/__init__.py +0 -0
  196. django_cfg/dashboard/management/commands/__init__.py +0 -0
  197. django_cfg/dashboard/management/commands/debug_dashboard.py +109 -0
  198. django_cfg/dashboard/sections/__init__.py +1 -0
  199. django_cfg/dashboard/sections/base.py +128 -0
  200. django_cfg/dashboard/sections/commands.py +32 -0
  201. django_cfg/dashboard/sections/overview.py +394 -0
  202. django_cfg/dashboard/sections/stats.py +48 -0
  203. django_cfg/dashboard/sections/system.py +73 -0
  204. django_cfg/management/commands/check_settings.py +6 -2
  205. django_cfg/management/commands/clear_constance.py +6 -1
  206. django_cfg/management/commands/create_token.py +5 -4
  207. django_cfg/management/commands/generate.py +5 -0
  208. django_cfg/management/commands/list_urls.py +7 -2
  209. django_cfg/management/commands/migrate_all.py +6 -2
  210. django_cfg/management/commands/migrator.py +6 -1
  211. django_cfg/management/commands/rundramatiq.py +6 -1
  212. django_cfg/management/commands/rundramatiq_simulator.py +11 -4
  213. django_cfg/management/commands/runserver_ngrok.py +9 -7
  214. django_cfg/management/commands/script.py +25 -21
  215. django_cfg/management/commands/show_config.py +6 -1
  216. django_cfg/management/commands/show_urls.py +8 -3
  217. django_cfg/management/commands/superuser.py +5 -4
  218. django_cfg/management/commands/task_clear.py +8 -3
  219. django_cfg/management/commands/task_status.py +8 -3
  220. django_cfg/management/commands/test_email.py +6 -1
  221. django_cfg/management/commands/test_telegram.py +6 -1
  222. django_cfg/management/commands/test_twilio.py +6 -1
  223. django_cfg/management/commands/tree.py +7 -4
  224. django_cfg/models/__init__.py +88 -3
  225. django_cfg/models/api/__init__.py +27 -0
  226. django_cfg/models/{api.py → api/config.py} +1 -1
  227. django_cfg/models/api/drf/__init__.py +21 -0
  228. django_cfg/models/api/drf/config.py +101 -0
  229. django_cfg/models/api/drf/redoc.py +31 -0
  230. django_cfg/models/api/drf/spectacular.py +129 -0
  231. django_cfg/models/api/drf/swagger.py +59 -0
  232. django_cfg/models/{api_keys.py → api/keys.py} +16 -6
  233. django_cfg/models/{limits.py → api/limits.py} +0 -1
  234. django_cfg/models/base/__init__.py +14 -0
  235. django_cfg/models/django/__init__.py +16 -0
  236. django_cfg/models/{constance.py → django/constance.py} +1 -1
  237. django_cfg/models/{environment.py → django/environment.py} +1 -1
  238. django_cfg/models/infrastructure/__init__.py +17 -0
  239. django_cfg/models/{cache.py → infrastructure/cache.py} +3 -2
  240. django_cfg/models/infrastructure/database/__init__.py +22 -0
  241. django_cfg/models/infrastructure/database/config.py +265 -0
  242. django_cfg/models/infrastructure/database/converters.py +91 -0
  243. django_cfg/models/infrastructure/database/parsers.py +96 -0
  244. django_cfg/models/infrastructure/database/routing.py +85 -0
  245. django_cfg/models/infrastructure/database/validators.py +170 -0
  246. django_cfg/models/{logging.py → infrastructure/logging.py} +1 -1
  247. django_cfg/models/{security.py → infrastructure/security.py} +2 -2
  248. django_cfg/models/ngrok/__init__.py +11 -0
  249. django_cfg/models/ngrok/auth.py +37 -0
  250. django_cfg/models/ngrok/config.py +77 -0
  251. django_cfg/models/ngrok/tunnel.py +35 -0
  252. django_cfg/models/payments/__init__.py +20 -0
  253. django_cfg/models/payments/api_keys.py +57 -0
  254. django_cfg/models/{payments.py → payments/config.py} +56 -154
  255. django_cfg/models/payments/providers/__init__.py +15 -0
  256. django_cfg/models/payments/providers/base.py +25 -0
  257. django_cfg/models/payments/providers/nowpayments.py +48 -0
  258. django_cfg/models/services/__init__.py +18 -0
  259. django_cfg/models/services/base.py +65 -0
  260. django_cfg/models/{email.py → services/email.py} +1 -1
  261. django_cfg/models/services/telegram.py +172 -0
  262. django_cfg/models/tasks/__init__.py +51 -0
  263. django_cfg/models/tasks/backends.py +250 -0
  264. django_cfg/models/tasks/config.py +314 -0
  265. django_cfg/models/tasks/utils.py +174 -0
  266. django_cfg/modules/base.py +18 -3
  267. django_cfg/modules/django_admin/decorators/actions.py +1 -1
  268. django_cfg/modules/django_admin/decorators/display.py +1 -1
  269. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +1 -1
  270. django_cfg/modules/django_cfg_rpc_client/README.md +346 -0
  271. django_cfg/modules/django_cfg_rpc_client/__init__.py +51 -0
  272. django_cfg/modules/django_cfg_rpc_client/client.py +540 -0
  273. django_cfg/modules/django_cfg_rpc_client/config.py +207 -0
  274. django_cfg/modules/django_cfg_rpc_client/dashboard/README.md +517 -0
  275. django_cfg/modules/django_cfg_rpc_client/dashboard/UNFOLD_INTEGRATION.md +439 -0
  276. django_cfg/modules/django_cfg_rpc_client/dashboard/__init__.py +11 -0
  277. django_cfg/modules/django_cfg_rpc_client/dashboard/apps.py +22 -0
  278. django_cfg/modules/django_cfg_rpc_client/dashboard/monitor.py +435 -0
  279. django_cfg/modules/django_cfg_rpc_client/dashboard/static/django_cfg_rpc_dashboard/js/dashboard.js +373 -0
  280. django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/base.html +76 -0
  281. django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/dashboard.html +200 -0
  282. django_cfg/modules/django_cfg_rpc_client/dashboard/urls.py +22 -0
  283. django_cfg/modules/django_cfg_rpc_client/dashboard/urls_admin.py +9 -0
  284. django_cfg/modules/django_cfg_rpc_client/dashboard/views.py +251 -0
  285. django_cfg/modules/django_cfg_rpc_client/exceptions.py +201 -0
  286. django_cfg/modules/django_drf_theme/CHANGELOG.md +210 -0
  287. django_cfg/modules/django_drf_theme/EXAMPLE.md +465 -0
  288. django_cfg/modules/django_drf_theme/IMPLEMENTATION.md +232 -0
  289. django_cfg/modules/django_drf_theme/README.md +207 -0
  290. django_cfg/modules/django_drf_theme/TAILWIND_CDN_GUIDE.md +274 -0
  291. django_cfg/modules/django_drf_theme/__init__.py +23 -0
  292. django_cfg/modules/django_drf_theme/apps.py +15 -0
  293. django_cfg/modules/django_drf_theme/renderers.py +58 -0
  294. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/api.html +375 -0
  295. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/base.html +938 -0
  296. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/filter_form.html +132 -0
  297. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/raw_data_form.html +123 -0
  298. django_cfg/modules/django_drf_theme/templatetags/__init__.py +1 -0
  299. django_cfg/modules/django_drf_theme/templatetags/tailwind_tags.py +57 -0
  300. django_cfg/modules/django_email/__init__.py +14 -0
  301. django_cfg/modules/{django_email.py → django_email/service.py} +78 -113
  302. django_cfg/modules/django_email/utils.py +40 -0
  303. django_cfg/modules/django_health/__init__.py +9 -0
  304. django_cfg/modules/{django_health.py → django_health/service.py} +23 -21
  305. django_cfg/modules/django_llm/llm/client.py +155 -550
  306. django_cfg/modules/django_llm/llm/embeddings/__init__.py +13 -0
  307. django_cfg/modules/django_llm/llm/embeddings/mock_embedder.py +106 -0
  308. django_cfg/modules/django_llm/llm/embeddings/openai_embedder.py +79 -0
  309. django_cfg/modules/django_llm/llm/models_api/__init__.py +9 -0
  310. django_cfg/modules/django_llm/llm/models_api/models_query.py +163 -0
  311. django_cfg/modules/django_llm/llm/providers/__init__.py +15 -0
  312. django_cfg/modules/django_llm/llm/providers/config_builder.py +103 -0
  313. django_cfg/modules/django_llm/llm/providers/provider_manager.py +148 -0
  314. django_cfg/modules/django_llm/llm/providers/provider_selector.py +60 -0
  315. django_cfg/modules/django_llm/llm/requests/__init__.py +15 -0
  316. django_cfg/modules/django_llm/llm/requests/cache_manager.py +170 -0
  317. django_cfg/modules/django_llm/llm/requests/chat_handler.py +199 -0
  318. django_cfg/modules/django_llm/llm/requests/embedding_handler.py +113 -0
  319. django_cfg/modules/django_llm/llm/responses/__init__.py +9 -0
  320. django_cfg/modules/django_llm/llm/responses/response_builder.py +131 -0
  321. django_cfg/modules/django_llm/llm/stats/__init__.py +9 -0
  322. django_cfg/modules/django_llm/llm/stats/stats_manager.py +107 -0
  323. django_cfg/modules/django_llm/translator/detectors/__init__.py +13 -0
  324. django_cfg/modules/django_llm/translator/detectors/language_detector.py +90 -0
  325. django_cfg/modules/django_llm/translator/detectors/script_detector.py +153 -0
  326. django_cfg/modules/django_llm/translator/stats/__init__.py +11 -0
  327. django_cfg/modules/django_llm/translator/stats/stats_tracker.py +85 -0
  328. django_cfg/modules/django_llm/translator/translator.py +150 -603
  329. django_cfg/modules/django_llm/translator/translators/__init__.py +15 -0
  330. django_cfg/modules/django_llm/translator/translators/json_translator.py +316 -0
  331. django_cfg/modules/django_llm/translator/translators/text_translator.py +139 -0
  332. django_cfg/modules/django_llm/translator/utils/__init__.py +13 -0
  333. django_cfg/modules/django_llm/translator/utils/prompt_builder.py +110 -0
  334. django_cfg/modules/django_llm/translator/utils/text_utils.py +114 -0
  335. django_cfg/modules/django_logging/FIXES_SUMMARY.md +276 -0
  336. django_cfg/modules/django_logging/LOGGING_GUIDE.md +504 -0
  337. django_cfg/modules/django_logging/__init__.py +14 -0
  338. django_cfg/modules/{django_logger.py → django_logging/django_logger.py} +13 -13
  339. django_cfg/modules/{logger.py → django_logging/logger.py} +14 -4
  340. django_cfg/modules/django_ngrok/__init__.py +39 -0
  341. django_cfg/modules/{django_ngrok.py → django_ngrok/service.py} +14 -42
  342. django_cfg/modules/django_rpc_old/POETRY.md +344 -0
  343. django_cfg/modules/django_rpc_old/README.md +397 -0
  344. django_cfg/modules/django_rpc_old/TESTING.md +358 -0
  345. django_cfg/modules/django_rpc_old/__init__.py +39 -0
  346. django_cfg/modules/django_rpc_old/client.py +531 -0
  347. django_cfg/modules/django_rpc_old/config.py +279 -0
  348. django_cfg/modules/django_rpc_old/exceptions.py +172 -0
  349. django_cfg/modules/django_tailwind/README.md +478 -0
  350. django_cfg/modules/django_tailwind/__init__.py +7 -0
  351. django_cfg/modules/django_tailwind/apps.py +10 -0
  352. django_cfg/modules/django_tailwind/templates/django_tailwind/app.html +5 -0
  353. django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +117 -0
  354. django_cfg/modules/django_tailwind/templates/django_tailwind/components/navbar.html +124 -0
  355. django_cfg/modules/django_tailwind/templates/django_tailwind/components/theme_toggle.html +54 -0
  356. django_cfg/modules/django_tailwind/templates/django_tailwind/components/user_menu.html +116 -0
  357. django_cfg/modules/django_tailwind/templates/django_tailwind/simple.html +46 -0
  358. django_cfg/modules/django_tailwind/templatetags/__init__.py +1 -0
  359. django_cfg/modules/django_tailwind/templatetags/tailwind_info.py +185 -0
  360. django_cfg/modules/django_tasks/__init__.py +29 -0
  361. django_cfg/modules/django_tasks/factory.py +127 -0
  362. django_cfg/modules/{django_tasks.py → django_tasks/service.py} +45 -274
  363. django_cfg/modules/django_tasks/settings.py +107 -0
  364. django_cfg/modules/django_telegram/__init__.py +29 -0
  365. django_cfg/modules/{django_telegram.py → django_telegram/service.py} +45 -113
  366. django_cfg/modules/django_telegram/utils.py +62 -0
  367. django_cfg/modules/django_twilio/__init__.py +54 -107
  368. django_cfg/modules/django_twilio/_imports.py +30 -0
  369. django_cfg/modules/django_twilio/base.py +192 -0
  370. django_cfg/modules/django_twilio/email_otp.py +227 -0
  371. django_cfg/modules/django_twilio/sendgrid_service.py +1 -1
  372. django_cfg/modules/django_twilio/simple_service.py +1 -2
  373. django_cfg/modules/django_twilio/sms.py +94 -0
  374. django_cfg/modules/django_twilio/twilio_service.py +2 -3
  375. django_cfg/modules/django_twilio/unified.py +310 -0
  376. django_cfg/modules/django_twilio/utils.py +190 -0
  377. django_cfg/modules/django_twilio/whatsapp.py +137 -0
  378. django_cfg/modules/django_unfold/callbacks/base.py +198 -7
  379. django_cfg/modules/django_unfold/callbacks/main.py +102 -10
  380. django_cfg/modules/django_unfold/dashboard.py +65 -43
  381. django_cfg/modules/django_unfold/models/config.py +13 -12
  382. django_cfg/modules/django_unfold/models/navigation.py +8 -3
  383. django_cfg/modules/django_unfold/models/tabs.py +2 -2
  384. django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +102 -0
  385. django_cfg/registry/core.py +24 -26
  386. django_cfg/registry/modules.py +5 -2
  387. django_cfg/registry/services.py +20 -3
  388. django_cfg/registry/third_party.py +8 -8
  389. django_cfg/static/admin/css/dashboard.css +260 -0
  390. django_cfg/static/admin/js/commands.js +171 -0
  391. django_cfg/static/admin/js/dashboard.js +126 -0
  392. django_cfg/templates/admin/components/management_commands.js +375 -0
  393. django_cfg/templates/admin/components/progress_bar.html +18 -23
  394. django_cfg/templates/admin/index.html +48 -20
  395. django_cfg/templates/admin/index_new.html +106 -0
  396. django_cfg/templates/admin/layouts/base_dashboard.html +60 -0
  397. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +1 -20
  398. django_cfg/templates/admin/sections/commands_section.html +626 -0
  399. django_cfg/templates/admin/sections/overview_section.html +112 -0
  400. django_cfg/templates/admin/sections/stats_section.html +35 -0
  401. django_cfg/templates/admin/sections/system_section.html +99 -0
  402. django_cfg/templates/admin/snippets/components/CHARTS_GUIDE.md +322 -0
  403. django_cfg/templates/admin/snippets/components/activity_tracker.html +85 -47
  404. django_cfg/templates/admin/snippets/components/charts_section.html +154 -64
  405. django_cfg/templates/admin/snippets/components/django_commands.html +3 -3
  406. django_cfg/templates/admin/snippets/components/recent_activity_improved.html +25 -0
  407. django_cfg/templates/admin/snippets/components/recent_users_table.html +1 -1
  408. django_cfg/templates/admin/snippets/components/system_metrics.html +179 -93
  409. django_cfg/templates/admin/snippets/zones/zones_table.html +2 -2
  410. django_cfg/templatetags/django_cfg.py +7 -1
  411. django_cfg/utils/smart_defaults.py +4 -4
  412. django_cfg-1.4.0.dist-info/METADATA +920 -0
  413. {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/RECORD +424 -195
  414. django_cfg/apps/accounts/utils/auth_email_service.py +0 -84
  415. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +0 -879
  416. django_cfg/core/generation.py +0 -621
  417. django_cfg/management/commands/validate_config.py +0 -189
  418. django_cfg/models/database.py +0 -480
  419. django_cfg/models/drf.py +0 -272
  420. django_cfg/models/ngrok.py +0 -122
  421. django_cfg/models/services.py +0 -440
  422. django_cfg/models/tasks.py +0 -550
  423. django_cfg/modules/django_twilio/service.py +0 -942
  424. django_cfg/template_archive/django_sample.zip +0 -0
  425. django_cfg/templates/rest_framework/api.html +0 -12
  426. django_cfg/utils/toolkit.py +0 -703
  427. django_cfg-1.3.13.dist-info/METADATA +0 -1029
  428. /django_cfg/apps/accounts/management/commands/{test_otp.py → otp_test.py} +0 -0
  429. /django_cfg/core/{environment.py → environment/detector.py} +0 -0
  430. /django_cfg/models/{cors.py → api/cors.py} +0 -0
  431. /django_cfg/models/{jwt.py → api/jwt.py} +0 -0
  432. /django_cfg/models/{base.py → base/config.py} +0 -0
  433. /django_cfg/models/{cfg.py → base/module.py} +0 -0
  434. /django_cfg/models/{revolution.py → django/revolution.py} +0 -0
  435. /django_cfg/modules/{dramatiq_setup.py → django_tasks/dramatiq_setup.py} +0 -0
  436. {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/WHEEL +0 -0
  437. {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/entry_points.txt +0 -0
  438. {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,531 @@
1
+ """
2
+ WebSocket RPC Client for Django.
3
+
4
+ Synchronous RPC client enabling Django applications to communicate
5
+ with external WebSocket servers via Redis.
6
+
7
+ File size: ~650 lines
8
+ """
9
+
10
+ import redis
11
+ import json
12
+ import logging
13
+ from uuid import uuid4
14
+ from typing import Optional, TypeVar, Type
15
+ from pydantic import BaseModel
16
+
17
+ from django_cfg.models.websocket import (
18
+ RPCRequest,
19
+ RPCResponse,
20
+ RPCError,
21
+ RPCErrorCode,
22
+ )
23
+ from .exceptions import (
24
+ RPCTimeoutError,
25
+ RPCRemoteError,
26
+ RPCConnectionError,
27
+ RPCConfigurationError,
28
+ )
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ TParams = TypeVar("TParams", bound=BaseModel)
33
+ TResult = TypeVar("TResult", bound=BaseModel)
34
+
35
+
36
+ class WebSocketRPCClient:
37
+ """
38
+ Synchronous RPC client for Django to communicate with WebSocket servers.
39
+
40
+ Features:
41
+ - Uses Redis Streams for reliable request delivery
42
+ - Uses Redis Lists for fast response retrieval
43
+ - Blocks synchronously using BLPOP (no async/await)
44
+ - Handles correlation IDs automatically
45
+ - Type-safe API with Pydantic models
46
+ - Connection pooling for performance
47
+ - Automatic cleanup of ephemeral keys
48
+
49
+ Example:
50
+ >>> from django_cfg.modules.django_rpc import get_rpc_client
51
+ >>> from django_cfg.models.websocket import NotificationRequest, NotificationResponse
52
+ >>>
53
+ >>> rpc = get_rpc_client()
54
+ >>> result: NotificationResponse = rpc.call(
55
+ ... method="send_notification",
56
+ ... params=NotificationRequest(user_id="123", message="Hello"),
57
+ ... result_model=NotificationResponse
58
+ ... )
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ redis_url: Optional[str] = None,
64
+ timeout: int = 30,
65
+ request_stream: str = "stream:requests",
66
+ consumer_group: str = "rpc_group",
67
+ stream_maxlen: int = 10000,
68
+ response_key_prefix: str = "list:response:",
69
+ response_key_ttl: int = 60,
70
+ max_connections: int = 50,
71
+ log_calls: bool = False,
72
+ ):
73
+ """
74
+ Initialize RPC client.
75
+
76
+ Args:
77
+ redis_url: Redis connection URL
78
+ timeout: Default timeout for RPC calls (seconds)
79
+ request_stream: Redis Stream name for requests
80
+ consumer_group: Consumer group name
81
+ stream_maxlen: Maximum stream length
82
+ response_key_prefix: Prefix for response list keys
83
+ response_key_ttl: Response key TTL (seconds)
84
+ max_connections: Maximum Redis connections in pool
85
+ log_calls: Log all RPC calls (verbose)
86
+ """
87
+ self.redis_url = redis_url or self._get_redis_url_from_settings()
88
+ self.default_timeout = timeout
89
+ self.request_stream = request_stream
90
+ self.consumer_group = consumer_group
91
+ self.stream_maxlen = stream_maxlen
92
+ self.response_key_prefix = response_key_prefix
93
+ self.response_key_ttl = response_key_ttl
94
+ self.log_calls = log_calls
95
+
96
+ # Create Redis connection pool
97
+ try:
98
+ self._pool = redis.ConnectionPool.from_url(
99
+ self.redis_url,
100
+ max_connections=max_connections,
101
+ decode_responses=False, # We handle JSON ourselves
102
+ socket_keepalive=True,
103
+ )
104
+ self._redis = redis.Redis(connection_pool=self._pool)
105
+
106
+ # Test connection
107
+ self._redis.ping()
108
+
109
+ logger.info(f"WebSocket RPC Client initialized: {self.redis_url}")
110
+
111
+ except redis.ConnectionError as e:
112
+ raise RPCConnectionError(
113
+ f"Failed to connect to Redis: {e}",
114
+ redis_url=self.redis_url,
115
+ )
116
+ except Exception as e:
117
+ raise RPCConnectionError(
118
+ f"Failed to initialize RPC client: {e}",
119
+ redis_url=self.redis_url,
120
+ )
121
+
122
+ def _get_redis_url_from_settings(self) -> str:
123
+ """
124
+ Get Redis URL from Django settings.
125
+
126
+ Returns:
127
+ Redis URL string
128
+
129
+ Raises:
130
+ RPCConfigurationError: If settings not configured
131
+ """
132
+ try:
133
+ from django.conf import settings
134
+
135
+ if not hasattr(settings, "WEBSOCKET_RPC"):
136
+ raise RPCConfigurationError(
137
+ "WEBSOCKET_RPC not found in Django settings. "
138
+ "Configure WebSocketRPCConfig in django-cfg.",
139
+ config_key="WEBSOCKET_RPC",
140
+ )
141
+
142
+ redis_url = settings.WEBSOCKET_RPC.get("REDIS_URL")
143
+ if not redis_url:
144
+ raise RPCConfigurationError(
145
+ "REDIS_URL not found in WEBSOCKET_RPC settings",
146
+ config_key="WEBSOCKET_RPC.REDIS_URL",
147
+ )
148
+
149
+ return redis_url
150
+
151
+ except ImportError:
152
+ raise RPCConfigurationError(
153
+ "Django not installed. Provide redis_url explicitly or configure Django."
154
+ )
155
+
156
+ def call(
157
+ self,
158
+ method: str,
159
+ params: TParams,
160
+ result_model: Type[TResult],
161
+ timeout: Optional[int] = None,
162
+ ) -> TResult:
163
+ """
164
+ Make synchronous RPC call to WebSocket server.
165
+
166
+ Args:
167
+ method: RPC method name
168
+ params: Pydantic model with parameters
169
+ result_model: Expected result model class
170
+ timeout: Optional timeout override (seconds)
171
+
172
+ Returns:
173
+ Pydantic result model instance
174
+
175
+ Raises:
176
+ RPCTimeoutError: If timeout exceeded
177
+ RPCRemoteError: If remote execution failed
178
+ ValidationError: If response doesn't match result_model
179
+
180
+ Example:
181
+ >>> from django_cfg.models.websocket import EchoParams, EchoResult
182
+ >>> result = rpc.call(
183
+ ... method="echo",
184
+ ... params=EchoParams(message="Hello"),
185
+ ... result_model=EchoResult,
186
+ ... timeout=10
187
+ ... )
188
+ >>> print(result.echoed) # "Hello"
189
+ """
190
+ timeout = timeout or self.default_timeout
191
+
192
+ # Generate correlation ID
193
+ cid = uuid4()
194
+ reply_key = f"{self.response_key_prefix}{cid}"
195
+
196
+ # Build RPC request
197
+ request = RPCRequest[type(params)](
198
+ correlation_id=cid,
199
+ method=method,
200
+ params=params,
201
+ reply_to=reply_key,
202
+ timeout=timeout,
203
+ )
204
+
205
+ if self.log_calls:
206
+ logger.debug(f"RPC call: {method} (cid={cid})")
207
+
208
+ try:
209
+ # Send request to Redis Stream
210
+ message_id = self._redis.xadd(
211
+ self.request_stream,
212
+ {"payload": request.model_dump_json()},
213
+ maxlen=self.stream_maxlen,
214
+ approximate=True,
215
+ )
216
+
217
+ if self.log_calls:
218
+ logger.debug(f"Request sent to stream: {message_id}")
219
+
220
+ # Block waiting for response (BLPOP)
221
+ response_data = self._redis.blpop(reply_key, timeout)
222
+
223
+ if response_data is None:
224
+ # Timeout occurred
225
+ logger.warning(f"RPC timeout: {method} (cid={cid}, timeout={timeout}s)")
226
+ raise RPCTimeoutError(
227
+ f"RPC call '{method}' timed out after {timeout}s",
228
+ method=method,
229
+ timeout_seconds=timeout,
230
+ )
231
+
232
+ # Unpack BLPOP result: (key, value)
233
+ _, response_json = response_data
234
+
235
+ # Parse response
236
+ response_dict = json.loads(response_json)
237
+ response = RPCResponse[result_model](**response_dict)
238
+
239
+ if self.log_calls:
240
+ logger.debug(f"RPC response: {method} (success={response.success})")
241
+
242
+ # Check for errors
243
+ if not response.success:
244
+ error = RPCError(
245
+ code=response.error_code or RPCErrorCode.INTERNAL_ERROR,
246
+ message=response.error or "Unknown error",
247
+ retryable=response.error_code in {
248
+ RPCErrorCode.TIMEOUT,
249
+ RPCErrorCode.SERVICE_UNAVAILABLE,
250
+ RPCErrorCode.RATE_LIMIT_EXCEEDED,
251
+ },
252
+ )
253
+ raise RPCRemoteError(error)
254
+
255
+ if response.result is None:
256
+ raise RPCRemoteError(
257
+ RPCError(
258
+ code=RPCErrorCode.INTERNAL_ERROR,
259
+ message="Response success=True but result is None",
260
+ )
261
+ )
262
+
263
+ return response.result
264
+
265
+ finally:
266
+ # Always cleanup response key
267
+ try:
268
+ self._redis.delete(reply_key)
269
+ except Exception as e:
270
+ logger.error(f"Failed to cleanup response key {reply_key}: {e}")
271
+
272
+ def fire_and_forget(self, method: str, params: TParams) -> str:
273
+ """
274
+ Send RPC request without waiting for response.
275
+
276
+ Useful for notifications where result doesn't matter.
277
+ Returns immediately after sending to Redis Stream.
278
+
279
+ Args:
280
+ method: RPC method name
281
+ params: Pydantic model with parameters
282
+
283
+ Returns:
284
+ Message ID from Redis Stream
285
+
286
+ Example:
287
+ >>> from pydantic import BaseModel
288
+ >>> class EventLog(BaseModel):
289
+ ... event: str
290
+ ... user_id: str
291
+ >>> rpc.fire_and_forget(
292
+ ... method="log_event",
293
+ ... params=EventLog(event="user_login", user_id="123")
294
+ ... )
295
+ """
296
+ cid = uuid4()
297
+
298
+ request = RPCRequest[type(params)](
299
+ correlation_id=cid,
300
+ method=method,
301
+ params=params,
302
+ reply_to=f"{self.response_key_prefix}{cid}", # Won't be used
303
+ timeout=0, # Indicates fire-and-forget
304
+ )
305
+
306
+ message_id = self._redis.xadd(
307
+ self.request_stream,
308
+ {"payload": request.model_dump_json()},
309
+ maxlen=self.stream_maxlen,
310
+ approximate=True,
311
+ )
312
+
313
+ if self.log_calls:
314
+ logger.debug(f"Fire-and-forget: {method} (mid={message_id})")
315
+
316
+ return message_id.decode()
317
+
318
+ def broadcast(self, channel: str, message: BaseModel) -> int:
319
+ """
320
+ Broadcast message via Redis Pub/Sub.
321
+
322
+ Sends message to all WebSocket servers subscribed to channel.
323
+
324
+ Args:
325
+ channel: Redis channel name
326
+ message: Pydantic model to broadcast
327
+
328
+ Returns:
329
+ Number of subscribers that received message
330
+
331
+ Example:
332
+ >>> from django_cfg.models.websocket import BroadcastRequest
333
+ >>> rpc.broadcast(
334
+ ... channel="notifications:broadcast",
335
+ ... message=BroadcastRequest(
336
+ ... target="all",
337
+ ... event_type="system_update",
338
+ ... payload={"version": "2.0"}
339
+ ... )
340
+ ... )
341
+ """
342
+ subscribers = self._redis.publish(channel, message.model_dump_json())
343
+
344
+ if self.log_calls:
345
+ logger.info(f"Broadcast to {channel}: {subscribers} subscribers")
346
+
347
+ return subscribers
348
+
349
+ def health_check(self, timeout: int = 5) -> bool:
350
+ """
351
+ Check if RPC system is healthy.
352
+
353
+ Attempts to:
354
+ 1. Ping Redis
355
+ 2. Send echo RPC call (if echo method exists)
356
+
357
+ Args:
358
+ timeout: Health check timeout (seconds)
359
+
360
+ Returns:
361
+ True if healthy, False otherwise
362
+
363
+ Example:
364
+ >>> if rpc.health_check():
365
+ ... print("RPC system healthy")
366
+ ... else:
367
+ ... print("RPC system unhealthy")
368
+ """
369
+ try:
370
+ # Try to ping Redis
371
+ ping_result = self._redis.ping()
372
+ if not ping_result:
373
+ logger.error("Health check failed: Redis ping returned False")
374
+ return False
375
+
376
+ # Try simple echo RPC (optional, depends on server implementation)
377
+ try:
378
+ from django_cfg.models.websocket import EchoParams, EchoResult
379
+
380
+ result = self.call(
381
+ method="echo",
382
+ params=EchoParams(message="health_check"),
383
+ result_model=EchoResult,
384
+ timeout=timeout,
385
+ )
386
+
387
+ if result.echoed != "health_check":
388
+ logger.error(
389
+ f"Health check failed: Echo mismatch "
390
+ f"(expected 'health_check', got '{result.echoed}')"
391
+ )
392
+ return False
393
+
394
+ except (ImportError, RPCTimeoutError, RPCRemoteError):
395
+ # Echo method may not be implemented, that's okay
396
+ # Redis ping success is minimum health check
397
+ pass
398
+
399
+ return True
400
+
401
+ except Exception as e:
402
+ logger.error(f"Health check failed: {e}")
403
+ return False
404
+
405
+ def get_connection_info(self) -> dict:
406
+ """
407
+ Get connection information.
408
+
409
+ Returns:
410
+ Dictionary with connection details
411
+
412
+ Example:
413
+ >>> info = rpc.get_connection_info()
414
+ >>> print(info["redis_url"])
415
+ >>> print(info["pool_size"])
416
+ """
417
+ return {
418
+ "redis_url": self.redis_url,
419
+ "pool_size": self._pool.max_connections if self._pool else 0,
420
+ "request_stream": self.request_stream,
421
+ "consumer_group": self.consumer_group,
422
+ "default_timeout": self.default_timeout,
423
+ }
424
+
425
+ def close(self):
426
+ """
427
+ Close Redis connection pool.
428
+
429
+ Call this when shutting down application to clean up resources.
430
+
431
+ Example:
432
+ >>> rpc.close()
433
+ """
434
+ if self._pool:
435
+ self._pool.disconnect()
436
+ logger.info("RPC client closed")
437
+
438
+ def __enter__(self):
439
+ """Context manager entry."""
440
+ return self
441
+
442
+ def __exit__(self, exc_type, exc_val, exc_tb):
443
+ """Context manager exit."""
444
+ self.close()
445
+
446
+
447
+ # ==================== Singleton Pattern ====================
448
+
449
+ _rpc_client: Optional[WebSocketRPCClient] = None
450
+ _rpc_client_lock = None
451
+
452
+
453
+ def get_rpc_client(force_new: bool = False) -> WebSocketRPCClient:
454
+ """
455
+ Get global RPC client instance (singleton).
456
+
457
+ Creates client from Django settings on first call.
458
+ Subsequent calls return the same instance (thread-safe).
459
+
460
+ Args:
461
+ force_new: Force create new instance (for testing)
462
+
463
+ Returns:
464
+ WebSocketRPCClient instance
465
+
466
+ Example:
467
+ >>> from django_cfg.modules.django_rpc import get_rpc_client
468
+ >>> rpc = get_rpc_client()
469
+ >>> result = rpc.call(...)
470
+ """
471
+ global _rpc_client, _rpc_client_lock
472
+
473
+ if force_new:
474
+ return _create_client_from_settings()
475
+
476
+ if _rpc_client is None:
477
+ # Thread-safe singleton creation
478
+ import threading
479
+
480
+ if _rpc_client_lock is None:
481
+ _rpc_client_lock = threading.Lock()
482
+
483
+ with _rpc_client_lock:
484
+ if _rpc_client is None:
485
+ _rpc_client = _create_client_from_settings()
486
+
487
+ return _rpc_client
488
+
489
+
490
+ def _create_client_from_settings() -> WebSocketRPCClient:
491
+ """
492
+ Create RPC client from Django settings.
493
+
494
+ Returns:
495
+ WebSocketRPCClient instance
496
+
497
+ Raises:
498
+ RPCConfigurationError: If settings not configured
499
+ """
500
+ try:
501
+ from django.conf import settings
502
+
503
+ if not hasattr(settings, "WEBSOCKET_RPC"):
504
+ raise RPCConfigurationError(
505
+ "WEBSOCKET_RPC not found in Django settings"
506
+ )
507
+
508
+ rpc_settings = settings.WEBSOCKET_RPC
509
+
510
+ return WebSocketRPCClient(
511
+ redis_url=rpc_settings.get("REDIS_URL"),
512
+ timeout=rpc_settings.get("RPC_TIMEOUT", 30),
513
+ request_stream=rpc_settings.get("REQUEST_STREAM", "stream:requests"),
514
+ consumer_group=rpc_settings.get("CONSUMER_GROUP", "rpc_group"),
515
+ stream_maxlen=rpc_settings.get("STREAM_MAXLEN", 10000),
516
+ response_key_prefix=rpc_settings.get("RESPONSE_KEY_PREFIX", "list:response:"),
517
+ response_key_ttl=rpc_settings.get("RESPONSE_KEY_TTL", 60),
518
+ max_connections=rpc_settings.get("REDIS_MAX_CONNECTIONS", 50),
519
+ log_calls=rpc_settings.get("LOG_RPC_CALLS", False),
520
+ )
521
+
522
+ except ImportError:
523
+ raise RPCConfigurationError(
524
+ "Django not installed. Cannot create client from settings."
525
+ )
526
+
527
+
528
+ __all__ = [
529
+ "WebSocketRPCClient",
530
+ "get_rpc_client",
531
+ ]