django-cfg 1.3.11__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 (439) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/inlines.py +11 -5
  3. django_cfg/apps/accounts/admin/user_admin.py +39 -16
  4. django_cfg/apps/accounts/serializers/profile.py +1 -1
  5. django_cfg/apps/accounts/services/otp_service.py +18 -11
  6. django_cfg/apps/accounts/signals.py +15 -24
  7. django_cfg/apps/accounts/utils/notifications.py +217 -358
  8. django_cfg/apps/accounts/views/otp.py +2 -2
  9. django_cfg/apps/accounts/views/webhook.py +1 -1
  10. django_cfg/apps/agents/core/django_agent.py +1 -1
  11. django_cfg/apps/api/commands/views.py +66 -83
  12. django_cfg/apps/api/health/drf_views.py +269 -0
  13. django_cfg/apps/api/health/serializers.py +45 -0
  14. django_cfg/apps/api/health/urls.py +6 -1
  15. django_cfg/apps/knowbase/admin/actions/__init__.py +13 -0
  16. django_cfg/apps/knowbase/admin/actions/visibility_actions.py +56 -0
  17. django_cfg/apps/knowbase/admin/document_admin.py +136 -270
  18. django_cfg/apps/knowbase/admin/helpers/__init__.py +17 -0
  19. django_cfg/apps/knowbase/admin/helpers/configs.py +72 -0
  20. django_cfg/apps/knowbase/admin/helpers/display_helpers.py +156 -0
  21. django_cfg/apps/knowbase/admin/helpers/statistics.py +108 -0
  22. django_cfg/apps/knowbase/config/constance_fields.py +1 -1
  23. django_cfg/apps/knowbase/config/settings.py +2 -2
  24. django_cfg/apps/knowbase/mixins/__init__.py +19 -2
  25. django_cfg/apps/knowbase/mixins/config/__init__.py +14 -0
  26. django_cfg/apps/knowbase/mixins/config/defaults.py +75 -0
  27. django_cfg/apps/knowbase/mixins/config/meta_config.py +120 -0
  28. django_cfg/apps/knowbase/mixins/creator.py +10 -10
  29. django_cfg/apps/knowbase/mixins/external_data_mixin.py +105 -403
  30. django_cfg/apps/knowbase/mixins/generators/__init__.py +16 -0
  31. django_cfg/apps/knowbase/mixins/generators/content_generator.py +218 -0
  32. django_cfg/apps/knowbase/mixins/generators/field_analyzer.py +76 -0
  33. django_cfg/apps/knowbase/mixins/generators/metadata_generator.py +124 -0
  34. django_cfg/apps/knowbase/mixins/service.py +2 -2
  35. django_cfg/apps/knowbase/services/archive/__init__.py +1 -0
  36. django_cfg/apps/knowbase/services/archive/analyzers/__init__.py +17 -0
  37. django_cfg/apps/knowbase/services/archive/analyzers/complexity_analyzer.py +33 -0
  38. django_cfg/apps/knowbase/services/archive/analyzers/purpose_detector.py +36 -0
  39. django_cfg/apps/knowbase/services/archive/analyzers/quality_analyzer.py +39 -0
  40. django_cfg/apps/knowbase/services/archive/analyzers/tag_generator.py +103 -0
  41. django_cfg/apps/knowbase/services/archive/chunking/__init__.py +19 -0
  42. django_cfg/apps/knowbase/services/archive/chunking/base.py +81 -0
  43. django_cfg/apps/knowbase/services/archive/chunking/json_chunker.py +62 -0
  44. django_cfg/apps/knowbase/services/archive/chunking/markdown_chunker.py +107 -0
  45. django_cfg/apps/knowbase/services/archive/chunking/python_chunker.py +248 -0
  46. django_cfg/apps/knowbase/services/archive/chunking/text_chunker.py +70 -0
  47. django_cfg/apps/knowbase/services/archive/chunking_service.py +110 -729
  48. django_cfg/apps/knowbase/services/archive/context/__init__.py +14 -0
  49. django_cfg/apps/knowbase/services/archive/context/builders.py +220 -0
  50. django_cfg/apps/knowbase/services/archive/context/models.py +38 -0
  51. django_cfg/apps/knowbase/services/embedding/models.py +18 -14
  52. django_cfg/apps/knowbase/services/embedding/processors.py +6 -3
  53. django_cfg/apps/knowbase/tasks/document_processing.py +11 -3
  54. django_cfg/apps/leads/tests.py +1 -1
  55. django_cfg/apps/payments/admin/api_keys_admin.py +1 -1
  56. django_cfg/apps/payments/admin/balance_admin.py +1 -1
  57. django_cfg/apps/payments/admin/currencies_admin.py +1 -1
  58. django_cfg/apps/payments/admin/payments_admin.py +1 -1
  59. django_cfg/apps/payments/admin/subscriptions_admin.py +1 -1
  60. django_cfg/apps/payments/admin_interface/templates/payments/base.html +59 -126
  61. django_cfg/apps/payments/admin_interface/views/api/payments.py +1 -1
  62. django_cfg/apps/payments/admin_interface/views/api/stats.py +1 -1
  63. django_cfg/apps/payments/admin_interface/views/api/users.py +1 -1
  64. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +1 -1
  65. django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +1 -1
  66. django_cfg/apps/payments/admin_interface/views/base.py +29 -2
  67. django_cfg/apps/payments/apps.py +1 -1
  68. django_cfg/apps/payments/config/django_cfg_integration.py +2 -2
  69. django_cfg/apps/payments/config/helpers.py +3 -2
  70. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +1 -1
  71. django_cfg/apps/payments/management/commands/currency_stats.py +1 -1
  72. django_cfg/apps/payments/management/commands/manage_currencies.py +1 -1
  73. django_cfg/apps/payments/management/commands/manage_providers.py +1 -1
  74. django_cfg/apps/payments/management/commands/process_pending_payments.py +1 -1
  75. django_cfg/apps/payments/management/commands/test_providers.py +1 -1
  76. django_cfg/apps/payments/middleware/api_access.py +1 -1
  77. django_cfg/apps/payments/middleware/rate_limiting.py +1 -1
  78. django_cfg/apps/payments/middleware/usage_tracking.py +1 -1
  79. django_cfg/apps/payments/models/balance.py +2 -2
  80. django_cfg/apps/payments/models/managers/api_key_managers.py +1 -1
  81. django_cfg/apps/payments/models/managers/balance_managers.py +1 -1
  82. django_cfg/apps/payments/models/managers/currency_managers.py +1 -1
  83. django_cfg/apps/payments/models/managers/payment_managers.py +1 -1
  84. django_cfg/apps/payments/models/managers/subscription_managers.py +1 -1
  85. django_cfg/apps/payments/models/payments.py +2 -2
  86. django_cfg/apps/payments/services/cache_service/__init__.py +1 -1
  87. django_cfg/apps/payments/services/cache_service/simple_cache.py +10 -5
  88. django_cfg/apps/payments/services/core/base.py +1 -1
  89. django_cfg/apps/payments/services/core/currency/__init__.py +13 -0
  90. django_cfg/apps/payments/services/core/currency/currency_converter.py +57 -0
  91. django_cfg/apps/payments/services/core/currency/currency_validator.py +61 -0
  92. django_cfg/apps/payments/services/core/operations/__init__.py +15 -0
  93. django_cfg/apps/payments/services/core/operations/payment_canceller.py +100 -0
  94. django_cfg/apps/payments/services/core/operations/payment_creator.py +196 -0
  95. django_cfg/apps/payments/services/core/operations/status_checker.py +100 -0
  96. django_cfg/apps/payments/services/core/payment_service.py +124 -612
  97. django_cfg/apps/payments/services/core/providers/__init__.py +13 -0
  98. django_cfg/apps/payments/services/core/providers/provider_client.py +132 -0
  99. django_cfg/apps/payments/services/core/providers/status_mapper.py +89 -0
  100. django_cfg/apps/payments/services/core/utils/__init__.py +13 -0
  101. django_cfg/apps/payments/services/core/utils/data_converter.py +48 -0
  102. django_cfg/apps/payments/services/core/utils/statistics_calculator.py +69 -0
  103. django_cfg/apps/payments/services/providers/base.py +1 -1
  104. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +3 -3
  105. django_cfg/apps/payments/services/providers/nowpayments/parsers/__init__.py +9 -0
  106. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/__init__.py +23 -0
  107. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/constants.py +23 -0
  108. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/currency_names.py +244 -0
  109. django_cfg/apps/payments/services/providers/nowpayments/parsers/data/patterns.py +511 -0
  110. django_cfg/apps/payments/services/providers/nowpayments/parsers/parser.py +168 -0
  111. django_cfg/apps/payments/services/providers/nowpayments/provider.py +1 -1
  112. django_cfg/apps/payments/services/providers/nowpayments/sync.py +1 -1
  113. django_cfg/apps/payments/services/providers/registry.py +1 -1
  114. django_cfg/apps/payments/services/providers/sync_service.py +1 -1
  115. django_cfg/apps/payments/signals/__init__.py +1 -1
  116. django_cfg/apps/payments/signals/api_key_signals.py +1 -1
  117. django_cfg/apps/payments/signals/balance_signals.py +1 -1
  118. django_cfg/apps/payments/signals/payment_signals.py +1 -1
  119. django_cfg/apps/payments/signals/subscription_signals.py +1 -1
  120. django_cfg/apps/payments/views/api/api_keys.py +1 -1
  121. django_cfg/apps/payments/views/api/balances.py +1 -1
  122. django_cfg/apps/payments/views/api/base.py +1 -1
  123. django_cfg/apps/payments/views/api/currencies.py +1 -1
  124. django_cfg/apps/payments/views/api/payments.py +1 -1
  125. django_cfg/apps/payments/views/api/subscriptions.py +1 -1
  126. django_cfg/apps/payments/views/api/webhooks.py +1 -1
  127. django_cfg/apps/payments/views/serializers/api_keys.py +1 -1
  128. django_cfg/apps/payments/views/serializers/balances.py +1 -1
  129. django_cfg/apps/payments/views/serializers/currencies.py +1 -1
  130. django_cfg/apps/payments/views/serializers/payments.py +1 -1
  131. django_cfg/apps/payments/views/serializers/subscriptions.py +1 -1
  132. django_cfg/apps/payments/views/serializers/webhooks.py +1 -1
  133. django_cfg/apps/support/admin/support_admin.py +21 -13
  134. django_cfg/apps/support/templates/support/chat/access_denied.html +21 -27
  135. django_cfg/apps/support/templates/support/chat/ticket_chat.html +183 -254
  136. django_cfg/apps/support/utils/support_email_service.py +1 -1
  137. django_cfg/apps/tasks/templates/tasks/layout/base.html +20 -115
  138. django_cfg/apps/tasks/utils/simulator.py +1 -1
  139. django_cfg/apps/tasks/views/dashboard.py +33 -3
  140. django_cfg/apps/urls.py +5 -1
  141. django_cfg/cli/README.md +57 -471
  142. django_cfg/cli/commands/create_project.py +140 -529
  143. django_cfg/cli/main.py +13 -10
  144. django_cfg/core/__init__.py +63 -6
  145. django_cfg/core/base/__init__.py +5 -0
  146. django_cfg/core/base/config_model.py +652 -0
  147. django_cfg/core/builders/__init__.py +11 -0
  148. django_cfg/core/builders/apps_builder.py +258 -0
  149. django_cfg/core/builders/middleware_builder.py +115 -0
  150. django_cfg/core/builders/security_builder.py +96 -0
  151. django_cfg/core/config.py +20 -892
  152. django_cfg/core/constants.py +69 -0
  153. django_cfg/core/environment/__init__.py +9 -0
  154. django_cfg/core/exceptions.py +45 -298
  155. django_cfg/core/generation/__init__.py +51 -0
  156. django_cfg/core/generation/core_generators/__init__.py +0 -0
  157. django_cfg/core/generation/core_generators/settings.py +90 -0
  158. django_cfg/core/generation/core_generators/static.py +82 -0
  159. django_cfg/core/generation/core_generators/templates.py +141 -0
  160. django_cfg/core/generation/data_generators/__init__.py +15 -0
  161. django_cfg/core/generation/data_generators/cache.py +132 -0
  162. django_cfg/core/generation/data_generators/database.py +117 -0
  163. django_cfg/core/generation/generation.py +92 -0
  164. django_cfg/core/generation/integration_generators/__init__.py +21 -0
  165. django_cfg/core/generation/integration_generators/api.py +237 -0
  166. django_cfg/core/generation/integration_generators/sessions.py +65 -0
  167. django_cfg/core/generation/integration_generators/tailwind.py +54 -0
  168. django_cfg/core/generation/integration_generators/tasks.py +92 -0
  169. django_cfg/core/generation/integration_generators/third_party.py +144 -0
  170. django_cfg/core/generation/orchestrator.py +285 -0
  171. django_cfg/core/generation/protocols.py +30 -0
  172. django_cfg/core/generation/security_generators/__init__.py +0 -0
  173. django_cfg/core/generation/utility_generators/__init__.py +24 -0
  174. django_cfg/core/generation/utility_generators/email.py +58 -0
  175. django_cfg/core/generation/utility_generators/i18n.py +66 -0
  176. django_cfg/core/generation/utility_generators/limits.py +58 -0
  177. django_cfg/core/generation/utility_generators/logging.py +66 -0
  178. django_cfg/core/generation/utility_generators/security.py +101 -0
  179. django_cfg/core/generation/utils/__init__.py +0 -0
  180. django_cfg/core/generation/utils/helpers.py +32 -0
  181. django_cfg/core/integration/__init__.py +18 -25
  182. django_cfg/core/integration/display/startup.py +146 -133
  183. django_cfg/core/integration/url_integration.py +13 -2
  184. django_cfg/core/services/__init__.py +5 -0
  185. django_cfg/core/services/config_service.py +121 -0
  186. django_cfg/core/state/__init__.py +9 -0
  187. django_cfg/core/state/registry.py +84 -0
  188. django_cfg/core/types/__init__.py +15 -0
  189. django_cfg/core/types/aliases.py +15 -0
  190. django_cfg/core/types/enums.py +49 -0
  191. django_cfg/dashboard/DEBUG_README.md +105 -0
  192. django_cfg/dashboard/REFACTORING_SUMMARY.md +237 -0
  193. django_cfg/dashboard/__init__.py +24 -0
  194. django_cfg/dashboard/components.py +308 -0
  195. django_cfg/dashboard/debug.py +176 -0
  196. django_cfg/dashboard/management/__init__.py +0 -0
  197. django_cfg/dashboard/management/commands/__init__.py +0 -0
  198. django_cfg/dashboard/management/commands/debug_dashboard.py +109 -0
  199. django_cfg/dashboard/sections/__init__.py +1 -0
  200. django_cfg/dashboard/sections/base.py +128 -0
  201. django_cfg/dashboard/sections/commands.py +32 -0
  202. django_cfg/dashboard/sections/overview.py +394 -0
  203. django_cfg/dashboard/sections/stats.py +48 -0
  204. django_cfg/dashboard/sections/system.py +73 -0
  205. django_cfg/management/commands/check_settings.py +6 -2
  206. django_cfg/management/commands/clear_constance.py +6 -1
  207. django_cfg/management/commands/create_token.py +5 -4
  208. django_cfg/management/commands/generate.py +5 -0
  209. django_cfg/management/commands/list_urls.py +7 -2
  210. django_cfg/management/commands/migrate_all.py +6 -2
  211. django_cfg/management/commands/migrator.py +6 -1
  212. django_cfg/management/commands/rundramatiq.py +6 -1
  213. django_cfg/management/commands/rundramatiq_simulator.py +11 -4
  214. django_cfg/management/commands/runserver_ngrok.py +9 -7
  215. django_cfg/management/commands/script.py +25 -21
  216. django_cfg/management/commands/show_config.py +6 -1
  217. django_cfg/management/commands/show_urls.py +8 -3
  218. django_cfg/management/commands/superuser.py +5 -4
  219. django_cfg/management/commands/task_clear.py +8 -3
  220. django_cfg/management/commands/task_status.py +8 -3
  221. django_cfg/management/commands/test_email.py +6 -1
  222. django_cfg/management/commands/test_telegram.py +6 -1
  223. django_cfg/management/commands/test_twilio.py +6 -1
  224. django_cfg/management/commands/tree.py +7 -4
  225. django_cfg/models/__init__.py +88 -3
  226. django_cfg/models/api/__init__.py +27 -0
  227. django_cfg/models/{api.py → api/config.py} +1 -1
  228. django_cfg/models/api/drf/__init__.py +21 -0
  229. django_cfg/models/api/drf/config.py +101 -0
  230. django_cfg/models/api/drf/redoc.py +31 -0
  231. django_cfg/models/api/drf/spectacular.py +129 -0
  232. django_cfg/models/api/drf/swagger.py +59 -0
  233. django_cfg/models/{api_keys.py → api/keys.py} +16 -6
  234. django_cfg/models/{limits.py → api/limits.py} +0 -1
  235. django_cfg/models/base/__init__.py +14 -0
  236. django_cfg/models/django/__init__.py +16 -0
  237. django_cfg/models/{constance.py → django/constance.py} +1 -1
  238. django_cfg/models/{environment.py → django/environment.py} +1 -1
  239. django_cfg/models/infrastructure/__init__.py +17 -0
  240. django_cfg/models/{cache.py → infrastructure/cache.py} +3 -2
  241. django_cfg/models/infrastructure/database/__init__.py +22 -0
  242. django_cfg/models/infrastructure/database/config.py +265 -0
  243. django_cfg/models/infrastructure/database/converters.py +91 -0
  244. django_cfg/models/infrastructure/database/parsers.py +96 -0
  245. django_cfg/models/infrastructure/database/routing.py +85 -0
  246. django_cfg/models/infrastructure/database/validators.py +170 -0
  247. django_cfg/models/{logging.py → infrastructure/logging.py} +1 -1
  248. django_cfg/models/{security.py → infrastructure/security.py} +2 -2
  249. django_cfg/models/ngrok/__init__.py +11 -0
  250. django_cfg/models/ngrok/auth.py +37 -0
  251. django_cfg/models/ngrok/config.py +77 -0
  252. django_cfg/models/ngrok/tunnel.py +35 -0
  253. django_cfg/models/payments/__init__.py +20 -0
  254. django_cfg/models/payments/api_keys.py +57 -0
  255. django_cfg/models/{payments.py → payments/config.py} +56 -154
  256. django_cfg/models/payments/providers/__init__.py +15 -0
  257. django_cfg/models/payments/providers/base.py +25 -0
  258. django_cfg/models/payments/providers/nowpayments.py +48 -0
  259. django_cfg/models/services/__init__.py +18 -0
  260. django_cfg/models/services/base.py +65 -0
  261. django_cfg/models/{email.py → services/email.py} +1 -1
  262. django_cfg/models/services/telegram.py +172 -0
  263. django_cfg/models/tasks/__init__.py +51 -0
  264. django_cfg/models/tasks/backends.py +250 -0
  265. django_cfg/models/tasks/config.py +314 -0
  266. django_cfg/models/tasks/utils.py +174 -0
  267. django_cfg/modules/base.py +18 -3
  268. django_cfg/modules/django_admin/decorators/actions.py +1 -1
  269. django_cfg/modules/django_admin/decorators/display.py +1 -1
  270. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +1 -1
  271. django_cfg/modules/django_cfg_rpc_client/README.md +346 -0
  272. django_cfg/modules/django_cfg_rpc_client/__init__.py +51 -0
  273. django_cfg/modules/django_cfg_rpc_client/client.py +540 -0
  274. django_cfg/modules/django_cfg_rpc_client/config.py +207 -0
  275. django_cfg/modules/django_cfg_rpc_client/dashboard/README.md +517 -0
  276. django_cfg/modules/django_cfg_rpc_client/dashboard/UNFOLD_INTEGRATION.md +439 -0
  277. django_cfg/modules/django_cfg_rpc_client/dashboard/__init__.py +11 -0
  278. django_cfg/modules/django_cfg_rpc_client/dashboard/apps.py +22 -0
  279. django_cfg/modules/django_cfg_rpc_client/dashboard/monitor.py +435 -0
  280. django_cfg/modules/django_cfg_rpc_client/dashboard/static/django_cfg_rpc_dashboard/js/dashboard.js +373 -0
  281. django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/base.html +76 -0
  282. django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/dashboard.html +200 -0
  283. django_cfg/modules/django_cfg_rpc_client/dashboard/urls.py +22 -0
  284. django_cfg/modules/django_cfg_rpc_client/dashboard/urls_admin.py +9 -0
  285. django_cfg/modules/django_cfg_rpc_client/dashboard/views.py +251 -0
  286. django_cfg/modules/django_cfg_rpc_client/exceptions.py +201 -0
  287. django_cfg/modules/django_drf_theme/CHANGELOG.md +210 -0
  288. django_cfg/modules/django_drf_theme/EXAMPLE.md +465 -0
  289. django_cfg/modules/django_drf_theme/IMPLEMENTATION.md +232 -0
  290. django_cfg/modules/django_drf_theme/README.md +207 -0
  291. django_cfg/modules/django_drf_theme/TAILWIND_CDN_GUIDE.md +274 -0
  292. django_cfg/modules/django_drf_theme/__init__.py +23 -0
  293. django_cfg/modules/django_drf_theme/apps.py +15 -0
  294. django_cfg/modules/django_drf_theme/renderers.py +58 -0
  295. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/api.html +375 -0
  296. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/base.html +938 -0
  297. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/filter_form.html +132 -0
  298. django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/raw_data_form.html +123 -0
  299. django_cfg/modules/django_drf_theme/templatetags/__init__.py +1 -0
  300. django_cfg/modules/django_drf_theme/templatetags/tailwind_tags.py +57 -0
  301. django_cfg/modules/django_email/__init__.py +14 -0
  302. django_cfg/modules/{django_email.py → django_email/service.py} +78 -113
  303. django_cfg/modules/django_email/utils.py +40 -0
  304. django_cfg/modules/django_health/__init__.py +9 -0
  305. django_cfg/modules/{django_health.py → django_health/service.py} +23 -21
  306. django_cfg/modules/django_llm/llm/client.py +155 -550
  307. django_cfg/modules/django_llm/llm/embeddings/__init__.py +13 -0
  308. django_cfg/modules/django_llm/llm/embeddings/mock_embedder.py +106 -0
  309. django_cfg/modules/django_llm/llm/embeddings/openai_embedder.py +79 -0
  310. django_cfg/modules/django_llm/llm/models_api/__init__.py +9 -0
  311. django_cfg/modules/django_llm/llm/models_api/models_query.py +163 -0
  312. django_cfg/modules/django_llm/llm/providers/__init__.py +15 -0
  313. django_cfg/modules/django_llm/llm/providers/config_builder.py +103 -0
  314. django_cfg/modules/django_llm/llm/providers/provider_manager.py +148 -0
  315. django_cfg/modules/django_llm/llm/providers/provider_selector.py +60 -0
  316. django_cfg/modules/django_llm/llm/requests/__init__.py +15 -0
  317. django_cfg/modules/django_llm/llm/requests/cache_manager.py +170 -0
  318. django_cfg/modules/django_llm/llm/requests/chat_handler.py +199 -0
  319. django_cfg/modules/django_llm/llm/requests/embedding_handler.py +113 -0
  320. django_cfg/modules/django_llm/llm/responses/__init__.py +9 -0
  321. django_cfg/modules/django_llm/llm/responses/response_builder.py +131 -0
  322. django_cfg/modules/django_llm/llm/stats/__init__.py +9 -0
  323. django_cfg/modules/django_llm/llm/stats/stats_manager.py +107 -0
  324. django_cfg/modules/django_llm/translator/detectors/__init__.py +13 -0
  325. django_cfg/modules/django_llm/translator/detectors/language_detector.py +90 -0
  326. django_cfg/modules/django_llm/translator/detectors/script_detector.py +153 -0
  327. django_cfg/modules/django_llm/translator/stats/__init__.py +11 -0
  328. django_cfg/modules/django_llm/translator/stats/stats_tracker.py +85 -0
  329. django_cfg/modules/django_llm/translator/translator.py +150 -603
  330. django_cfg/modules/django_llm/translator/translators/__init__.py +15 -0
  331. django_cfg/modules/django_llm/translator/translators/json_translator.py +316 -0
  332. django_cfg/modules/django_llm/translator/translators/text_translator.py +139 -0
  333. django_cfg/modules/django_llm/translator/utils/__init__.py +13 -0
  334. django_cfg/modules/django_llm/translator/utils/prompt_builder.py +110 -0
  335. django_cfg/modules/django_llm/translator/utils/text_utils.py +114 -0
  336. django_cfg/modules/django_logging/FIXES_SUMMARY.md +276 -0
  337. django_cfg/modules/django_logging/LOGGING_GUIDE.md +504 -0
  338. django_cfg/modules/django_logging/__init__.py +14 -0
  339. django_cfg/modules/{django_logger.py → django_logging/django_logger.py} +13 -13
  340. django_cfg/modules/{logger.py → django_logging/logger.py} +14 -4
  341. django_cfg/modules/django_ngrok/__init__.py +39 -0
  342. django_cfg/modules/{django_ngrok.py → django_ngrok/service.py} +14 -42
  343. django_cfg/modules/django_rpc_old/POETRY.md +344 -0
  344. django_cfg/modules/django_rpc_old/README.md +397 -0
  345. django_cfg/modules/django_rpc_old/TESTING.md +358 -0
  346. django_cfg/modules/django_rpc_old/__init__.py +39 -0
  347. django_cfg/modules/django_rpc_old/client.py +531 -0
  348. django_cfg/modules/django_rpc_old/config.py +279 -0
  349. django_cfg/modules/django_rpc_old/exceptions.py +172 -0
  350. django_cfg/modules/django_tailwind/README.md +478 -0
  351. django_cfg/modules/django_tailwind/__init__.py +7 -0
  352. django_cfg/modules/django_tailwind/apps.py +10 -0
  353. django_cfg/modules/django_tailwind/templates/django_tailwind/app.html +5 -0
  354. django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +117 -0
  355. django_cfg/modules/django_tailwind/templates/django_tailwind/components/navbar.html +124 -0
  356. django_cfg/modules/django_tailwind/templates/django_tailwind/components/theme_toggle.html +54 -0
  357. django_cfg/modules/django_tailwind/templates/django_tailwind/components/user_menu.html +116 -0
  358. django_cfg/modules/django_tailwind/templates/django_tailwind/simple.html +46 -0
  359. django_cfg/modules/django_tailwind/templatetags/__init__.py +1 -0
  360. django_cfg/modules/django_tailwind/templatetags/tailwind_info.py +185 -0
  361. django_cfg/modules/django_tasks/__init__.py +29 -0
  362. django_cfg/modules/django_tasks/factory.py +127 -0
  363. django_cfg/modules/{django_tasks.py → django_tasks/service.py} +45 -274
  364. django_cfg/modules/django_tasks/settings.py +107 -0
  365. django_cfg/modules/django_telegram/__init__.py +29 -0
  366. django_cfg/modules/{django_telegram.py → django_telegram/service.py} +45 -113
  367. django_cfg/modules/django_telegram/utils.py +62 -0
  368. django_cfg/modules/django_twilio/__init__.py +54 -107
  369. django_cfg/modules/django_twilio/_imports.py +30 -0
  370. django_cfg/modules/django_twilio/base.py +192 -0
  371. django_cfg/modules/django_twilio/email_otp.py +227 -0
  372. django_cfg/modules/django_twilio/sendgrid_service.py +1 -1
  373. django_cfg/modules/django_twilio/simple_service.py +1 -2
  374. django_cfg/modules/django_twilio/sms.py +94 -0
  375. django_cfg/modules/django_twilio/twilio_service.py +2 -3
  376. django_cfg/modules/django_twilio/unified.py +310 -0
  377. django_cfg/modules/django_twilio/utils.py +190 -0
  378. django_cfg/modules/django_twilio/whatsapp.py +137 -0
  379. django_cfg/modules/django_unfold/callbacks/base.py +198 -7
  380. django_cfg/modules/django_unfold/callbacks/main.py +102 -10
  381. django_cfg/modules/django_unfold/dashboard.py +65 -43
  382. django_cfg/modules/django_unfold/models/config.py +13 -12
  383. django_cfg/modules/django_unfold/models/navigation.py +8 -3
  384. django_cfg/modules/django_unfold/models/tabs.py +2 -2
  385. django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +102 -0
  386. django_cfg/registry/core.py +24 -26
  387. django_cfg/registry/modules.py +5 -2
  388. django_cfg/registry/services.py +20 -3
  389. django_cfg/registry/third_party.py +8 -8
  390. django_cfg/static/admin/css/dashboard.css +260 -0
  391. django_cfg/static/admin/js/commands.js +171 -0
  392. django_cfg/static/admin/js/dashboard.js +126 -0
  393. django_cfg/templates/admin/components/management_commands.js +375 -0
  394. django_cfg/templates/admin/components/progress_bar.html +18 -23
  395. django_cfg/templates/admin/index.html +48 -20
  396. django_cfg/templates/admin/index_new.html +106 -0
  397. django_cfg/templates/admin/layouts/base_dashboard.html +60 -0
  398. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +1 -20
  399. django_cfg/templates/admin/sections/commands_section.html +626 -0
  400. django_cfg/templates/admin/sections/overview_section.html +112 -0
  401. django_cfg/templates/admin/sections/stats_section.html +35 -0
  402. django_cfg/templates/admin/sections/system_section.html +99 -0
  403. django_cfg/templates/admin/snippets/components/CHARTS_GUIDE.md +322 -0
  404. django_cfg/templates/admin/snippets/components/activity_tracker.html +85 -47
  405. django_cfg/templates/admin/snippets/components/charts_section.html +154 -64
  406. django_cfg/templates/admin/snippets/components/django_commands.html +3 -3
  407. django_cfg/templates/admin/snippets/components/recent_activity_improved.html +25 -0
  408. django_cfg/templates/admin/snippets/components/recent_users_table.html +1 -1
  409. django_cfg/templates/admin/snippets/components/system_metrics.html +179 -93
  410. django_cfg/templates/admin/snippets/zones/zones_table.html +2 -2
  411. django_cfg/templatetags/django_cfg.py +7 -1
  412. django_cfg/utils/smart_defaults.py +4 -4
  413. django_cfg-1.4.0.dist-info/METADATA +920 -0
  414. {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/RECORD +425 -196
  415. django_cfg/apps/accounts/utils/auth_email_service.py +0 -84
  416. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +0 -879
  417. django_cfg/core/generation.py +0 -621
  418. django_cfg/management/commands/validate_config.py +0 -189
  419. django_cfg/models/database.py +0 -480
  420. django_cfg/models/drf.py +0 -272
  421. django_cfg/models/ngrok.py +0 -122
  422. django_cfg/models/services.py +0 -440
  423. django_cfg/models/tasks.py +0 -550
  424. django_cfg/modules/django_twilio/service.py +0 -942
  425. django_cfg/template_archive/django_sample.zip +0 -0
  426. django_cfg/templates/rest_framework/api.html +0 -12
  427. django_cfg/utils/toolkit.py +0 -703
  428. django_cfg-1.3.11.dist-info/METADATA +0 -1029
  429. /django_cfg/apps/accounts/management/commands/{test_otp.py → otp_test.py} +0 -0
  430. /django_cfg/core/{environment.py → environment/detector.py} +0 -0
  431. /django_cfg/models/{cors.py → api/cors.py} +0 -0
  432. /django_cfg/models/{jwt.py → api/jwt.py} +0 -0
  433. /django_cfg/models/{base.py → base/config.py} +0 -0
  434. /django_cfg/models/{cfg.py → base/module.py} +0 -0
  435. /django_cfg/models/{revolution.py → django/revolution.py} +0 -0
  436. /django_cfg/modules/{dramatiq_setup.py → django_tasks/dramatiq_setup.py} +0 -0
  437. {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/WHEEL +0 -0
  438. {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/entry_points.txt +0 -0
  439. {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,13 @@
1
+ """
2
+ Embedding generation strategies for LLM client.
3
+
4
+ Provides real and mock embedding implementations.
5
+ """
6
+
7
+ from .openai_embedder import OpenAIEmbedder
8
+ from .mock_embedder import MockEmbedder
9
+
10
+ __all__ = [
11
+ 'OpenAIEmbedder',
12
+ 'MockEmbedder',
13
+ ]
@@ -0,0 +1,106 @@
1
+ """
2
+ Mock embedder for providers without embedding support.
3
+
4
+ Generates mock embeddings using MD5 hash for OpenRouter and similar providers.
5
+ """
6
+
7
+ import time
8
+ import hashlib
9
+ import logging
10
+ from typing import Optional
11
+
12
+ from ..costs import calculate_embedding_cost
13
+ from ..models import EmbeddingResponse
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class MockEmbedder:
19
+ """Generates mock embeddings for providers without embedding support."""
20
+
21
+ # Standard embedding dimension for ada-002 compatibility
22
+ EMBEDDING_DIMENSION = 1536
23
+
24
+ def __init__(self, models_cache=None):
25
+ """
26
+ Initialize mock embedder.
27
+
28
+ Args:
29
+ models_cache: Optional models cache for cost calculation
30
+ """
31
+ self.models_cache = models_cache
32
+
33
+ def generate(self, text: str, model: str) -> EmbeddingResponse:
34
+ """
35
+ Generate mock embedding using MD5 hash.
36
+
37
+ This is a workaround for OpenRouter which doesn't support embeddings.
38
+ The mock embedding is deterministic based on text content.
39
+
40
+ Args:
41
+ text: Text to generate embedding for
42
+ model: Model name (used for cost estimation)
43
+
44
+ Returns:
45
+ EmbeddingResponse with mock embedding vector and warning
46
+ """
47
+ start_time = time.time()
48
+
49
+ logger.warning(
50
+ "Using mock embedding generation. "
51
+ "OpenRouter doesn't support embedding models."
52
+ )
53
+
54
+ # Create mock embedding from text hash
55
+ mock_embedding = self._create_mock_vector(text)
56
+
57
+ # Estimate tokens and cost
58
+ tokens_used = len(text.split()) # Rough estimate
59
+ cost = calculate_embedding_cost(tokens_used, model, self.models_cache)
60
+
61
+ response_time = time.time() - start_time
62
+
63
+ logger.debug(
64
+ f"Generated mock embedding: {tokens_used} tokens (estimated), "
65
+ f"${cost:.6f}, {response_time:.2f}s"
66
+ )
67
+
68
+ return EmbeddingResponse(
69
+ embedding=mock_embedding,
70
+ tokens=tokens_used,
71
+ cost=cost,
72
+ model=model,
73
+ text_length=len(text),
74
+ dimension=len(mock_embedding),
75
+ response_time=response_time,
76
+ warning="Mock embedding - OpenRouter doesn't support embedding models"
77
+ )
78
+
79
+ def _create_mock_vector(self, text: str) -> list:
80
+ """
81
+ Create mock embedding vector from text hash.
82
+
83
+ Uses MD5 hash to create a deterministic vector that's consistent
84
+ for the same text but different for different texts.
85
+
86
+ Args:
87
+ text: Input text
88
+
89
+ Returns:
90
+ List of floats representing the mock embedding
91
+ """
92
+ # Generate MD5 hash of text
93
+ text_hash = hashlib.md5(text.encode()).hexdigest()
94
+
95
+ # Convert hex pairs to normalized floats (0.0 - 1.0)
96
+ mock_embedding = [
97
+ float(int(text_hash[i:i+2], 16)) / 255.0
98
+ for i in range(0, min(32, len(text_hash)), 2)
99
+ ]
100
+
101
+ # Pad to standard embedding size
102
+ while len(mock_embedding) < self.EMBEDDING_DIMENSION:
103
+ mock_embedding.append(0.0)
104
+
105
+ # Truncate to exact dimension
106
+ return mock_embedding[:self.EMBEDDING_DIMENSION]
@@ -0,0 +1,79 @@
1
+ """
2
+ OpenAI embedder for real embedding generation.
3
+
4
+ Uses OpenAI Embeddings API to generate high-quality embeddings.
5
+ """
6
+
7
+ import time
8
+ import logging
9
+ from typing import Optional
10
+
11
+ from ..costs import calculate_embedding_cost
12
+ from ..models import EmbeddingResponse
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class OpenAIEmbedder:
18
+ """Generates real embeddings using OpenAI API."""
19
+
20
+ def __init__(self, models_cache=None):
21
+ """
22
+ Initialize OpenAI embedder.
23
+
24
+ Args:
25
+ models_cache: Optional models cache for cost calculation
26
+ """
27
+ self.models_cache = models_cache
28
+
29
+ def generate(self, client, text: str, model: str) -> EmbeddingResponse:
30
+ """
31
+ Generate real embedding via OpenAI API.
32
+
33
+ Args:
34
+ client: OpenAI client instance
35
+ text: Text to generate embedding for
36
+ model: Embedding model to use
37
+
38
+ Returns:
39
+ EmbeddingResponse with embedding vector and metadata
40
+ """
41
+ start_time = time.time()
42
+
43
+ # Remove provider prefix if present
44
+ api_model = model
45
+ if model.startswith("openai/"):
46
+ api_model = model.replace("openai/", "")
47
+
48
+ logger.debug(f"Generating embedding with model: {api_model}")
49
+
50
+ # Make API call
51
+ response = client.embeddings.create(
52
+ input=text,
53
+ model=api_model
54
+ )
55
+
56
+ # Extract embedding data
57
+ embedding_data = response.data[0]
58
+ embedding_vector = embedding_data.embedding
59
+
60
+ # Calculate tokens and cost
61
+ tokens_used = response.usage.total_tokens
62
+ cost = calculate_embedding_cost(tokens_used, model, self.models_cache)
63
+
64
+ response_time = time.time() - start_time
65
+
66
+ logger.debug(
67
+ f"Generated embedding: {tokens_used} tokens, "
68
+ f"${cost:.6f}, {response_time:.2f}s"
69
+ )
70
+
71
+ return EmbeddingResponse(
72
+ embedding=embedding_vector,
73
+ tokens=tokens_used,
74
+ cost=cost,
75
+ model=model,
76
+ text_length=len(text),
77
+ dimension=len(embedding_vector),
78
+ response_time=response_time
79
+ )
@@ -0,0 +1,9 @@
1
+ """
2
+ Models API for LLM client.
3
+
4
+ Provides query interface to models cache.
5
+ """
6
+
7
+ from .models_query import ModelsQueryAPI
8
+
9
+ __all__ = ['ModelsQueryAPI']
@@ -0,0 +1,163 @@
1
+ """
2
+ Models query API for LLM client.
3
+
4
+ Provides simplified interface to query models cache.
5
+ """
6
+
7
+ import logging
8
+ from typing import Dict, List, Optional, Any
9
+
10
+ from ..models_cache import ModelsCache, ModelInfo
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class ModelsQueryAPI:
16
+ """Provides query interface to models cache."""
17
+
18
+ def __init__(self, models_cache: Optional[ModelsCache] = None):
19
+ """
20
+ Initialize models query API.
21
+
22
+ Args:
23
+ models_cache: Optional models cache instance
24
+ """
25
+ self.models_cache = models_cache
26
+
27
+ async def fetch_models(self, force_refresh: bool = False) -> Dict[str, ModelInfo]:
28
+ """
29
+ Fetch available models with pricing information.
30
+
31
+ Args:
32
+ force_refresh: Force refresh even if cache is valid
33
+
34
+ Returns:
35
+ Dictionary of model_id -> ModelInfo
36
+ """
37
+ if not self.models_cache:
38
+ logger.warning("Models cache not available for this provider")
39
+ return {}
40
+
41
+ return await self.models_cache.fetch_models(force_refresh=force_refresh)
42
+
43
+ def get_model_info(self, model_id: str) -> Optional[ModelInfo]:
44
+ """
45
+ Get information about a specific model.
46
+
47
+ Args:
48
+ model_id: Model identifier
49
+
50
+ Returns:
51
+ ModelInfo or None if not found
52
+ """
53
+ if not self.models_cache:
54
+ return None
55
+
56
+ return self.models_cache.get_model(model_id)
57
+
58
+ def get_models_by_price(
59
+ self,
60
+ min_price: float = 0.0,
61
+ max_price: float = float('inf')
62
+ ) -> List[ModelInfo]:
63
+ """
64
+ Get models within a price range.
65
+
66
+ Args:
67
+ min_price: Minimum price
68
+ max_price: Maximum price
69
+
70
+ Returns:
71
+ List of ModelInfo objects
72
+ """
73
+ if not self.models_cache:
74
+ return []
75
+
76
+ return self.models_cache.get_models_by_price_range(min_price, max_price)
77
+
78
+ def get_free_models(self) -> List[ModelInfo]:
79
+ """
80
+ Get all free models.
81
+
82
+ Returns:
83
+ List of free ModelInfo objects
84
+ """
85
+ if not self.models_cache:
86
+ return []
87
+
88
+ return self.models_cache.get_free_models()
89
+
90
+ def get_budget_models(self, max_price: float = 1.0) -> List[ModelInfo]:
91
+ """
92
+ Get budget-friendly models.
93
+
94
+ Args:
95
+ max_price: Maximum price threshold
96
+
97
+ Returns:
98
+ List of budget ModelInfo objects
99
+ """
100
+ if not self.models_cache:
101
+ return []
102
+
103
+ return self.models_cache.get_budget_models(max_price)
104
+
105
+ def get_premium_models(self, min_price: float = 10.0) -> List[ModelInfo]:
106
+ """
107
+ Get premium models.
108
+
109
+ Args:
110
+ min_price: Minimum price threshold
111
+
112
+ Returns:
113
+ List of premium ModelInfo objects
114
+ """
115
+ if not self.models_cache:
116
+ return []
117
+
118
+ return self.models_cache.get_premium_models(min_price)
119
+
120
+ def search_models(self, query: str) -> List[ModelInfo]:
121
+ """
122
+ Search models by name, description, or tags.
123
+
124
+ Args:
125
+ query: Search query string
126
+
127
+ Returns:
128
+ List of matching ModelInfo objects
129
+ """
130
+ if not self.models_cache:
131
+ return []
132
+
133
+ return self.models_cache.search_models(query)
134
+
135
+ def get_models_summary(self) -> Dict[str, Any]:
136
+ """
137
+ Get summary of available models.
138
+
139
+ Returns:
140
+ Dictionary with models summary statistics
141
+ """
142
+ if not self.models_cache:
143
+ return {"error": "Models cache not available for this provider"}
144
+
145
+ return self.models_cache.get_models_summary()
146
+
147
+ def get_models_cache_info(self) -> Dict[str, Any]:
148
+ """
149
+ Get models cache information.
150
+
151
+ Returns:
152
+ Dictionary with cache information
153
+ """
154
+ if not self.models_cache:
155
+ return {"error": "Models cache not available for this provider"}
156
+
157
+ return self.models_cache.get_cache_info()
158
+
159
+ def clear_models_cache(self):
160
+ """Clear the models cache."""
161
+ if self.models_cache:
162
+ self.models_cache.clear_cache()
163
+ logger.info("Models cache cleared")
@@ -0,0 +1,15 @@
1
+ """
2
+ Provider management for LLM client.
3
+
4
+ Handles initialization, configuration, and selection of LLM providers.
5
+ """
6
+
7
+ from .config_builder import ConfigBuilder
8
+ from .provider_manager import ProviderManager
9
+ from .provider_selector import ProviderSelector
10
+
11
+ __all__ = [
12
+ 'ConfigBuilder',
13
+ 'ProviderManager',
14
+ 'ProviderSelector',
15
+ ]
@@ -0,0 +1,103 @@
1
+ """
2
+ Configuration builder for LLM providers.
3
+
4
+ Builds provider-specific configurations and headers.
5
+ """
6
+
7
+ import logging
8
+ from typing import Dict, Any, Optional
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class ConfigBuilder:
14
+ """Builds configuration for LLM providers."""
15
+
16
+ @staticmethod
17
+ def get_openrouter_headers(django_config: Optional[Any] = None) -> Dict[str, str]:
18
+ """
19
+ Build headers for OpenRouter API.
20
+
21
+ Args:
22
+ django_config: Django configuration object
23
+
24
+ Returns:
25
+ Dictionary of headers for OpenRouter
26
+ """
27
+ headers = {}
28
+
29
+ if django_config:
30
+ try:
31
+ site_url = getattr(django_config, 'site_url', 'http://localhost:8000')
32
+ project_name = getattr(django_config, 'project_name', 'Django CFG')
33
+ headers.update({
34
+ "HTTP-Referer": site_url,
35
+ "X-Title": project_name
36
+ })
37
+ except Exception as e:
38
+ logger.warning(f"Failed to extract config for OpenRouter headers: {e}")
39
+
40
+ return headers
41
+
42
+ @staticmethod
43
+ def get_provider_config(
44
+ provider: str,
45
+ django_config: Optional[Any] = None
46
+ ) -> Dict[str, Any]:
47
+ """
48
+ Get full provider configuration.
49
+
50
+ Args:
51
+ provider: Provider name ("openai" or "openrouter")
52
+ django_config: Django configuration object
53
+
54
+ Returns:
55
+ Provider configuration dictionary
56
+
57
+ Raises:
58
+ ValueError: If provider is not supported
59
+ """
60
+ base_configs = {
61
+ "openrouter": {
62
+ "base_url": "https://openrouter.ai/api/v1",
63
+ "headers": ConfigBuilder.get_openrouter_headers(django_config)
64
+ },
65
+ "openai": {
66
+ "base_url": "https://api.openai.com/v1",
67
+ "headers": {}
68
+ }
69
+ }
70
+
71
+ if provider not in base_configs:
72
+ raise ValueError(f"Unsupported provider: {provider}")
73
+
74
+ config = base_configs[provider].copy()
75
+
76
+ # Add custom headers from LLM config if available
77
+ if django_config:
78
+ try:
79
+ if hasattr(django_config, 'llm') and django_config.llm:
80
+ llm_config = django_config.llm
81
+ if hasattr(llm_config, 'custom_headers'):
82
+ config["headers"].update(llm_config.custom_headers)
83
+ except Exception as e:
84
+ logger.warning(f"Failed to add custom headers: {e}")
85
+
86
+ return config
87
+
88
+ @staticmethod
89
+ def get_default_model(provider: str) -> str:
90
+ """
91
+ Get default model for provider.
92
+
93
+ Args:
94
+ provider: Provider name
95
+
96
+ Returns:
97
+ Default model ID
98
+ """
99
+ default_models = {
100
+ "openrouter": "openai/gpt-4o-mini",
101
+ "openai": "gpt-4o-mini"
102
+ }
103
+ return default_models.get(provider, "gpt-4o-mini")
@@ -0,0 +1,148 @@
1
+ """
2
+ Provider manager for LLM client.
3
+
4
+ Manages initialization and access to LLM provider clients.
5
+ """
6
+
7
+ import logging
8
+ from typing import Dict, Optional, Any
9
+ from openai import OpenAI
10
+
11
+ from .config_builder import ConfigBuilder
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class ProviderManager:
17
+ """Manages LLM provider clients and API keys."""
18
+
19
+ def __init__(
20
+ self,
21
+ apikey_openrouter: Optional[str] = None,
22
+ apikey_openai: Optional[str] = None,
23
+ preferred_provider: Optional[str] = None,
24
+ django_config: Optional[Any] = None
25
+ ):
26
+ """
27
+ Initialize provider manager.
28
+
29
+ Args:
30
+ apikey_openrouter: OpenRouter API key
31
+ apikey_openai: OpenAI API key
32
+ preferred_provider: Preferred provider ("openai" or "openrouter")
33
+ django_config: Django configuration object
34
+ """
35
+ self.apikey_openrouter = apikey_openrouter
36
+ self.apikey_openai = apikey_openai
37
+ self.preferred_provider = preferred_provider
38
+ self.django_config = django_config
39
+
40
+ # Initialize clients dictionary
41
+ self.clients: Dict[str, OpenAI] = {}
42
+
43
+ # Initialize clients for available providers
44
+ self._init_openrouter_client()
45
+ self._init_openai_client()
46
+
47
+ # Determine primary provider
48
+ self.primary_provider = self._determine_primary_provider()
49
+
50
+ # Get primary client
51
+ self.primary_client = self.clients[self.primary_provider]
52
+
53
+ logger.info(f"Initialized ProviderManager with primary provider: {self.primary_provider}")
54
+
55
+ def _init_openrouter_client(self):
56
+ """Initialize OpenRouter client if API key is available."""
57
+ if self.apikey_openrouter:
58
+ try:
59
+ headers = ConfigBuilder.get_openrouter_headers(self.django_config)
60
+ self.clients["openrouter"] = OpenAI(
61
+ base_url="https://openrouter.ai/api/v1",
62
+ api_key=self.apikey_openrouter,
63
+ default_headers=headers
64
+ )
65
+ logger.info("OpenRouter client initialized")
66
+ except Exception as e:
67
+ logger.error(f"Failed to initialize OpenRouter client: {e}")
68
+
69
+ def _init_openai_client(self):
70
+ """Initialize OpenAI client if API key is available."""
71
+ if self.apikey_openai:
72
+ try:
73
+ self.clients["openai"] = OpenAI(api_key=self.apikey_openai)
74
+ logger.info("OpenAI client initialized")
75
+ except Exception as e:
76
+ logger.error(f"Failed to initialize OpenAI client: {e}")
77
+
78
+ def _determine_primary_provider(self) -> str:
79
+ """
80
+ Determine primary provider based on preference and available keys.
81
+
82
+ Returns:
83
+ Primary provider name
84
+
85
+ Raises:
86
+ ValueError: If no provider is available
87
+ """
88
+ # If preferred provider is explicitly set and available, use it
89
+ if self.preferred_provider:
90
+ if self.preferred_provider == "openai" and "openai" in self.clients:
91
+ return "openai"
92
+ elif self.preferred_provider == "openrouter" and "openrouter" in self.clients:
93
+ return "openrouter"
94
+ else:
95
+ logger.warning(
96
+ f"Preferred provider '{self.preferred_provider}' not available, "
97
+ f"falling back to auto-detection"
98
+ )
99
+
100
+ # Auto-detection: prefer OpenAI for better compatibility
101
+ if "openai" in self.clients:
102
+ return "openai"
103
+ elif "openrouter" in self.clients:
104
+ return "openrouter"
105
+ else:
106
+ raise ValueError(
107
+ "At least one API key (openrouter or openai) must be provided"
108
+ )
109
+
110
+ def get_client(self, provider: Optional[str] = None) -> OpenAI:
111
+ """
112
+ Get client for specific provider or primary.
113
+
114
+ Args:
115
+ provider: Provider name (None for primary)
116
+
117
+ Returns:
118
+ OpenAI client instance
119
+
120
+ Raises:
121
+ ValueError: If provider is not available
122
+ """
123
+ if provider:
124
+ if provider not in self.clients:
125
+ raise ValueError(f"Provider '{provider}' not available")
126
+ return self.clients[provider]
127
+ return self.primary_client
128
+
129
+ def has_provider(self, provider: str) -> bool:
130
+ """
131
+ Check if provider is available.
132
+
133
+ Args:
134
+ provider: Provider name
135
+
136
+ Returns:
137
+ True if provider is available
138
+ """
139
+ return provider in self.clients
140
+
141
+ def get_available_providers(self) -> list:
142
+ """
143
+ Get list of available providers.
144
+
145
+ Returns:
146
+ List of provider names
147
+ """
148
+ return list(self.clients.keys())
@@ -0,0 +1,60 @@
1
+ """
2
+ Provider selector for LLM client.
3
+
4
+ Selects optimal provider for specific tasks.
5
+ """
6
+
7
+ import logging
8
+ from typing import TYPE_CHECKING
9
+
10
+ if TYPE_CHECKING:
11
+ from .provider_manager import ProviderManager
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class ProviderSelector:
17
+ """Selects optimal provider for specific tasks."""
18
+
19
+ def __init__(self, provider_manager: 'ProviderManager'):
20
+ """
21
+ Initialize provider selector.
22
+
23
+ Args:
24
+ provider_manager: ProviderManager instance
25
+ """
26
+ self.provider_manager = provider_manager
27
+
28
+ def get_provider_for_task(self, task: str = "chat") -> str:
29
+ """
30
+ Get the best provider for a specific task.
31
+
32
+ Args:
33
+ task: Task type ("chat", "embedding", "completion")
34
+
35
+ Returns:
36
+ Provider name for the task
37
+ """
38
+ # For embeddings, always prefer OpenAI if available
39
+ # OpenRouter doesn't have proper embedding support
40
+ if task == "embedding" and self.provider_manager.has_provider("openai"):
41
+ logger.debug("Selecting OpenAI for embedding task")
42
+ return "openai"
43
+
44
+ # For other tasks, use primary provider
45
+ provider = self.provider_manager.primary_provider
46
+ logger.debug(f"Selecting {provider} for {task} task")
47
+ return provider
48
+
49
+ def should_use_mock_embedding(self, provider: str) -> bool:
50
+ """
51
+ Determine if mock embedding should be used for provider.
52
+
53
+ Args:
54
+ provider: Provider name
55
+
56
+ Returns:
57
+ True if mock embedding should be used
58
+ """
59
+ # OpenRouter doesn't support real embeddings
60
+ return provider == "openrouter"