codex-platform 0.5.1__tar.gz → 0.5.2__tar.gz

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 (210) hide show
  1. {codex_platform-0.5.1 → codex_platform-0.5.2}/CHANGELOG.md +11 -0
  2. {codex_platform-0.5.1 → codex_platform-0.5.2}/PKG-INFO +1 -1
  3. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/index.md +2 -1
  4. codex_platform-0.5.2/docs/en/api/messaging/audience.md +5 -0
  5. codex_platform-0.5.2/docs/en/api/messaging/campaigns/dto.md +5 -0
  6. codex_platform-0.5.2/docs/en/api/messaging/campaigns/index.md +5 -0
  7. codex_platform-0.5.2/docs/en/api/messaging/campaigns/protocols.md +5 -0
  8. codex_platform-0.5.2/docs/en/api/messaging/channels.md +5 -0
  9. codex_platform-0.5.2/docs/en/api/messaging/clients/smtp.md +5 -0
  10. codex_platform-0.5.2/docs/en/api/messaging/delivery/arq.md +5 -0
  11. codex_platform-0.5.2/docs/en/api/messaging/delivery/base.md +5 -0
  12. codex_platform-0.5.2/docs/en/api/messaging/delivery/direct.md +5 -0
  13. codex_platform-0.5.2/docs/en/api/messaging/dto.md +5 -0
  14. codex_platform-0.5.2/docs/en/api/messaging/index.md +8 -0
  15. codex_platform-0.5.2/docs/en/api/messaging/orchestrator.md +5 -0
  16. codex_platform-0.5.2/docs/en/api/messaging/registry.md +5 -0
  17. codex_platform-0.5.2/docs/en/api/messaging/renderer.md +5 -0
  18. codex_platform-0.5.2/docs/en/api/messaging/threading.md +5 -0
  19. codex_platform-0.5.2/docs/en/api/messaging/workers_contract.md +5 -0
  20. codex_platform-0.5.2/docs/en/api/notifications/index.md +8 -0
  21. {codex_platform-0.5.1 → codex_platform-0.5.2}/mkdocs.yml +20 -0
  22. codex_platform-0.5.2/src/codex_platform/messaging/__init__.py +68 -0
  23. codex_platform-0.5.2/src/codex_platform/messaging/audience.py +23 -0
  24. codex_platform-0.5.2/src/codex_platform/messaging/campaigns/__init__.py +6 -0
  25. codex_platform-0.5.2/src/codex_platform/messaging/campaigns/dto.py +46 -0
  26. codex_platform-0.5.2/src/codex_platform/messaging/campaigns/protocols.py +18 -0
  27. {codex_platform-0.5.1/src/codex_platform/notifications → codex_platform-0.5.2/src/codex_platform/messaging}/channels.py +1 -5
  28. {codex_platform-0.5.1/src/codex_platform/notifications → codex_platform-0.5.2/src/codex_platform/messaging}/clients/__init__.py +2 -0
  29. {codex_platform-0.5.1/src/codex_platform/notifications → codex_platform-0.5.2/src/codex_platform/messaging}/clients/smtp.py +30 -48
  30. codex_platform-0.5.2/src/codex_platform/messaging/delivery/__init__.py +11 -0
  31. codex_platform-0.5.2/src/codex_platform/messaging/delivery/arq.py +38 -0
  32. codex_platform-0.5.2/src/codex_platform/messaging/delivery/base.py +16 -0
  33. codex_platform-0.5.2/src/codex_platform/messaging/delivery/direct.py +76 -0
  34. codex_platform-0.5.2/src/codex_platform/messaging/dto.py +60 -0
  35. {codex_platform-0.5.1/src/codex_platform/notifications → codex_platform-0.5.2/src/codex_platform/messaging}/interfaces.py +6 -8
  36. codex_platform-0.5.2/src/codex_platform/messaging/orchestrator.py +101 -0
  37. codex_platform-0.5.2/src/codex_platform/messaging/registry.py +48 -0
  38. codex_platform-0.5.2/src/codex_platform/messaging/renderer.py +71 -0
  39. codex_platform-0.5.2/src/codex_platform/messaging/threading.py +65 -0
  40. codex_platform-0.5.2/src/codex_platform/messaging/workers_contract.py +14 -0
  41. codex_platform-0.5.2/src/codex_platform/notifications/__init__.py +13 -0
  42. codex_platform-0.5.2/src/codex_platform/notifications/channels.py +13 -0
  43. codex_platform-0.5.2/src/codex_platform/notifications/clients/__init__.py +13 -0
  44. codex_platform-0.5.2/src/codex_platform/notifications/clients/smtp.py +19 -0
  45. codex_platform-0.5.2/src/codex_platform/notifications/delivery/__init__.py +13 -0
  46. codex_platform-0.5.2/src/codex_platform/notifications/delivery/arq.py +13 -0
  47. codex_platform-0.5.2/src/codex_platform/notifications/delivery/base.py +13 -0
  48. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/notifications/delivery/direct.py +16 -36
  49. codex_platform-0.5.2/src/codex_platform/notifications/dto.py +13 -0
  50. codex_platform-0.5.2/src/codex_platform/notifications/interfaces.py +13 -0
  51. codex_platform-0.5.2/src/codex_platform/notifications/orchestrator.py +13 -0
  52. codex_platform-0.5.2/src/codex_platform/notifications/registry.py +13 -0
  53. codex_platform-0.5.2/src/codex_platform/notifications/renderer.py +13 -0
  54. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/streams/consumer.py +9 -3
  55. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/streams/processor.py +7 -2
  56. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/streams/runtime.py +2 -0
  57. codex_platform-0.5.2/tests/unit/test_messaging_api.py +185 -0
  58. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_stream_consumer.py +39 -2
  59. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_stream_processor.py +41 -0
  60. codex_platform-0.5.1/docs/en/api/notifications/index.md +0 -5
  61. codex_platform-0.5.1/src/codex_platform/notifications/__init__.py +0 -48
  62. codex_platform-0.5.1/src/codex_platform/notifications/delivery/__init__.py +0 -18
  63. codex_platform-0.5.1/src/codex_platform/notifications/delivery/arq.py +0 -69
  64. codex_platform-0.5.1/src/codex_platform/notifications/delivery/base.py +0 -39
  65. codex_platform-0.5.1/src/codex_platform/notifications/dto.py +0 -63
  66. codex_platform-0.5.1/src/codex_platform/notifications/orchestrator.py +0 -116
  67. codex_platform-0.5.1/src/codex_platform/notifications/registry.py +0 -71
  68. codex_platform-0.5.1/src/codex_platform/notifications/renderer.py +0 -112
  69. {codex_platform-0.5.1 → codex_platform-0.5.2}/.agents/rules/graphify.md +0 -0
  70. {codex_platform-0.5.1 → codex_platform-0.5.2}/.github/workflows/ci.yml +0 -0
  71. {codex_platform-0.5.1 → codex_platform-0.5.2}/.github/workflows/docs.yml +0 -0
  72. {codex_platform-0.5.1 → codex_platform-0.5.2}/.github/workflows/publish.yml +0 -0
  73. {codex_platform-0.5.1 → codex_platform-0.5.2}/.gitignore +0 -0
  74. {codex_platform-0.5.1 → codex_platform-0.5.2}/.pre-commit-config.yaml +0 -0
  75. {codex_platform-0.5.1 → codex_platform-0.5.2}/.python-version +0 -0
  76. {codex_platform-0.5.1 → codex_platform-0.5.2}/AGENTS.md +0 -0
  77. {codex_platform-0.5.1 → codex_platform-0.5.2}/CLAUDE.md +0 -0
  78. {codex_platform-0.5.1 → codex_platform-0.5.2}/README.md +0 -0
  79. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/changelog.md +0 -0
  80. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/dev/messaging/00_overview.md +0 -0
  81. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/dev/messaging/01_core_dtos_and_protocols.md +0 -0
  82. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/dev/messaging/02_channels_and_registry.md +0 -0
  83. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/dev/messaging/03_renderer_and_templates.md +0 -0
  84. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/dev/messaging/04_threading_and_headers.md +0 -0
  85. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/dev/messaging/05_workers_contract.md +0 -0
  86. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/dev/messaging/06_migration_from_notifications.md +0 -0
  87. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/notifications/channels.md +0 -0
  88. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/notifications/delivery/arq.md +0 -0
  89. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/notifications/delivery/base.md +0 -0
  90. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/notifications/delivery/direct.md +0 -0
  91. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/notifications/dto.md +0 -0
  92. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/notifications/orchestrator.md +0 -0
  93. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/notifications/registry.md +0 -0
  94. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/notifications/renderer.md +0 -0
  95. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/base.md +0 -0
  96. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/index.md +0 -0
  97. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/keys.md +0 -0
  98. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/operations/hash.md +0 -0
  99. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/operations/json_module.md +0 -0
  100. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/operations/json_string.md +0 -0
  101. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/operations/list_.md +0 -0
  102. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/operations/pipeline.md +0 -0
  103. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/operations/set_.md +0 -0
  104. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/operations/string.md +0 -0
  105. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/operations/zset.md +0 -0
  106. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/redis_service/service.md +0 -0
  107. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/streams/consumer.md +0 -0
  108. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/streams/dispatcher.md +0 -0
  109. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/streams/index.md +0 -0
  110. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/streams/processor.md +0 -0
  111. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/streams/producer.md +0 -0
  112. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/streams/router.md +0 -0
  113. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/workers/arq/base.md +0 -0
  114. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/workers/arq/config.md +0 -0
  115. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/workers/arq/index.md +0 -0
  116. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/workers/arq/task_utils.md +0 -0
  117. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/api/workers/arq/types.md +0 -0
  118. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/architecture/notifications/README.md +0 -0
  119. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/architecture/notifications/data_flow.md +0 -0
  120. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/architecture/redis_service/README.md +0 -0
  121. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/architecture/redis_service/data_flow.md +0 -0
  122. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/architecture/streams/README.md +0 -0
  123. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/architecture/streams/data_flow.md +0 -0
  124. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/architecture/workers/README.md +0 -0
  125. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/en/architecture/workers/data_flow.md +0 -0
  126. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/index.md +0 -0
  127. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/ru/architecture/notifications/README.md +0 -0
  128. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/ru/architecture/notifications/data_flow.md +0 -0
  129. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/ru/architecture/redis_service/README.md +0 -0
  130. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/ru/architecture/redis_service/data_flow.md +0 -0
  131. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/ru/architecture/streams/README.md +0 -0
  132. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/ru/architecture/streams/data_flow.md +0 -0
  133. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/ru/architecture/workers/README.md +0 -0
  134. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/ru/architecture/workers/data_flow.md +0 -0
  135. {codex_platform-0.5.1 → codex_platform-0.5.2}/docs/stylesheets/extra.css +0 -0
  136. {codex_platform-0.5.1 → codex_platform-0.5.2}/project_structure.txt +0 -0
  137. {codex_platform-0.5.1 → codex_platform-0.5.2}/pyproject.toml +0 -0
  138. {codex_platform-0.5.1 → codex_platform-0.5.2}/redis-backends-recovery.md +0 -0
  139. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/__init__.py +0 -0
  140. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/py.typed +0 -0
  141. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/__init__.py +0 -0
  142. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/base.py +0 -0
  143. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/exceptions.py +0 -0
  144. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/keys.py +0 -0
  145. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/managers/__init__.py +0 -0
  146. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/managers/base_manager.py +0 -0
  147. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/managers/site_settings.py +0 -0
  148. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/operations/__init__.py +0 -0
  149. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/operations/hash.py +0 -0
  150. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/operations/json_module.py +0 -0
  151. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/operations/json_string.py +0 -0
  152. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/operations/list_.py +0 -0
  153. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/operations/pipeline.py +0 -0
  154. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/operations/set_.py +0 -0
  155. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/operations/string.py +0 -0
  156. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/operations/sync_hash.py +0 -0
  157. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/operations/sync_string.py +0 -0
  158. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/operations/zset.py +0 -0
  159. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/redis_service/service.py +0 -0
  160. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/streams/__init__.py +0 -0
  161. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/streams/codec.py +0 -0
  162. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/streams/dispatcher.py +0 -0
  163. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/streams/producer.py +0 -0
  164. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/streams/router.py +0 -0
  165. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/workers/__init__.py +0 -0
  166. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/workers/arq/__init__.py +0 -0
  167. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/workers/arq/base.py +0 -0
  168. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/workers/arq/config.py +0 -0
  169. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/workers/arq/task_utils.py +0 -0
  170. {codex_platform-0.5.1 → codex_platform-0.5.2}/src/codex_platform/workers/arq/types.py +0 -0
  171. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/integration/__init__.py +0 -0
  172. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/integration/conftest.py +0 -0
  173. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/integration/test_docs_build.py +0 -0
  174. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/integration/test_redis_service.py +0 -0
  175. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/integration/test_streams.py +0 -0
  176. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/__init__.py +0 -0
  177. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/conftest.py +0 -0
  178. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_arq_base.py +0 -0
  179. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_arq_config.py +0 -0
  180. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_arq_public_api.py +0 -0
  181. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_arq_task_utils.py +0 -0
  182. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_notification_adapters.py +0 -0
  183. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_notification_channels.py +0 -0
  184. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_notification_dto.py +0 -0
  185. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_notification_orchestrator.py +0 -0
  186. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_notification_registry.py +0 -0
  187. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_notification_renderer.py +0 -0
  188. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_notification_smtp.py +0 -0
  189. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_redis_base.py +0 -0
  190. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_redis_hash.py +0 -0
  191. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_redis_keys.py +0 -0
  192. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_redis_list.py +0 -0
  193. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_redis_manager.py +0 -0
  194. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_redis_pipeline.py +0 -0
  195. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_redis_service.py +0 -0
  196. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_redis_set.py +0 -0
  197. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_redis_string.py +0 -0
  198. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_redis_zset.py +0 -0
  199. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_site_settings_manager.py +0 -0
  200. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_stream_dispatcher.py +0 -0
  201. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_stream_producer.py +0 -0
  202. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_stream_router.py +0 -0
  203. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_stream_runtime.py +0 -0
  204. {codex_platform-0.5.1 → codex_platform-0.5.2}/tests/unit/test_sync_operations.py +0 -0
  205. {codex_platform-0.5.1 → codex_platform-0.5.2}/tools/__init__.py +0 -0
  206. {codex_platform-0.5.1 → codex_platform-0.5.2}/tools/dev/README.md +0 -0
  207. {codex_platform-0.5.1 → codex_platform-0.5.2}/tools/dev/__init__.py +0 -0
  208. {codex_platform-0.5.1 → codex_platform-0.5.2}/tools/dev/check.py +0 -0
  209. {codex_platform-0.5.1 → codex_platform-0.5.2}/tools/dev/generate_project_tree.py +0 -0
  210. {codex_platform-0.5.1 → codex_platform-0.5.2}/uv.lock +0 -0
@@ -5,6 +5,17 @@ Grouped by `Added` · `Changed` · `Deprecated` · `Removed` · `Fixed`.
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.5.2] - 2026-05-07
9
+
10
+ ### Added
11
+ - Added the framework-agnostic `codex_platform.messaging` package with notification delivery, campaign, threading, renderer, and SMTP primitives.
12
+
13
+ ### Deprecated
14
+ - Deprecated `codex_platform.notifications` modules in favor of `codex_platform.messaging` compatibility re-exports.
15
+
16
+ ### Fixed
17
+ - Reduced Redis Streams consumer latency by using configurable blocking `XREADGROUP` reads instead of relying on the processor polling interval for normal wakeups.
18
+
8
19
  ## [0.5.1] - 2026-04-30
9
20
 
10
21
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-platform
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: Modular async infrastructure (Redis, Streams, ARQ, Notifications) built for Codex, designed for independent use.
5
5
  Project-URL: Homepage, https://github.com/codexdlc/codex-platform
6
6
  Project-URL: Documentation, https://codexdlc.github.io/codex-platform/
@@ -6,7 +6,8 @@ Auto-generated from source docstrings (Google style). If code behavior contradic
6
6
 
7
7
  | Module | Description |
8
8
  | :--- | :--- |
9
- | [notifications](notifications/index.md) | Multi-channel notification engine |
9
+ | [messaging](messaging/index.md) | Framework-agnostic messaging engine |
10
+ | [notifications](notifications/index.md) | Deprecated compatibility path for messaging |
10
11
  | [redis_service](redis_service/index.md) | Async Redis abstraction with typed operations |
11
12
  | [streams](streams/index.md) | Redis Streams producer/consumer event bus |
12
13
  | [workers.arq](workers/arq/index.md) | ARQ background worker base infrastructure |
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.audience
4
+
5
+ ::: codex_platform.messaging.audience
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.campaigns.dto
4
+
5
+ ::: codex_platform.messaging.campaigns.dto
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.campaigns
4
+
5
+ ::: codex_platform.messaging.campaigns
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.campaigns.protocols
4
+
5
+ ::: codex_platform.messaging.campaigns.protocols
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.channels
4
+
5
+ ::: codex_platform.messaging.channels
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.clients.smtp
4
+
5
+ ::: codex_platform.messaging.clients.smtp
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.delivery.arq
4
+
5
+ ::: codex_platform.messaging.delivery.arq
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.delivery.base
4
+
5
+ ::: codex_platform.messaging.delivery.base
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.delivery.direct
4
+
5
+ ::: codex_platform.messaging.delivery.direct
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.dto
4
+
5
+ ::: codex_platform.messaging.dto
@@ -0,0 +1,8 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging
4
+
5
+ Framework-agnostic messaging primitives. This package is the primary import
6
+ path for the former `codex_platform.notifications` API.
7
+
8
+ ::: codex_platform.messaging
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.orchestrator
4
+
5
+ ::: codex_platform.messaging.orchestrator
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.registry
4
+
5
+ ::: codex_platform.messaging.registry
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.renderer
4
+
5
+ ::: codex_platform.messaging.renderer
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.threading
4
+
5
+ ::: codex_platform.messaging.threading
@@ -0,0 +1,5 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # messaging.workers_contract
4
+
5
+ ::: codex_platform.messaging.workers_contract
@@ -0,0 +1,8 @@
1
+ <!-- type: REFERENCE -->
2
+
3
+ # notifications
4
+
5
+ Deprecated compatibility path. New code should import from
6
+ `codex_platform.messaging`.
7
+
8
+ ::: codex_platform.notifications
@@ -100,6 +100,26 @@ nav:
100
100
  - Поток данных: ru/architecture/workers/data_flow.md
101
101
  - API Reference:
102
102
  - Overview: en/api/index.md
103
+ - Messaging:
104
+ - Overview: en/api/messaging/index.md
105
+ - DTOs: en/api/messaging/dto.md
106
+ - Channels: en/api/messaging/channels.md
107
+ - Orchestrator: en/api/messaging/orchestrator.md
108
+ - Registry: en/api/messaging/registry.md
109
+ - Renderer: en/api/messaging/renderer.md
110
+ - Threading: en/api/messaging/threading.md
111
+ - Audience: en/api/messaging/audience.md
112
+ - Workers Contract: en/api/messaging/workers_contract.md
113
+ - Clients:
114
+ - SMTP: en/api/messaging/clients/smtp.md
115
+ - Campaigns:
116
+ - Overview: en/api/messaging/campaigns/index.md
117
+ - DTOs: en/api/messaging/campaigns/dto.md
118
+ - Protocols: en/api/messaging/campaigns/protocols.md
119
+ - Delivery:
120
+ - Base Protocol: en/api/messaging/delivery/base.md
121
+ - ARQ: en/api/messaging/delivery/arq.md
122
+ - Direct: en/api/messaging/delivery/direct.md
103
123
  - Notifications:
104
124
  - Overview: en/api/notifications/index.md
105
125
  - DTOs: en/api/notifications/dto.md
@@ -0,0 +1,68 @@
1
+ """Framework-agnostic messaging primitives for codex-platform."""
2
+
3
+ from .audience import AudienceBuilder
4
+ from .campaigns import CampaignBatchDTO, CampaignDispatcher, CampaignRecipientDraft
5
+ from .channels import NotificationChannel
6
+ from .clients.smtp import AsyncEmailClient
7
+ from .delivery import ArqNotificationAdapter, DirectNotificationAdapter, NotificationAdapter
8
+ from .dto import (
9
+ NotificationPayloadDTO,
10
+ NotificationRecipient,
11
+ RenderedNotificationDTO,
12
+ TemplateNotificationDTO,
13
+ ThreadHeadersDTO,
14
+ )
15
+ from .interfaces import ContentCacheAdapter, ContentProvider
16
+ from .orchestrator import BaseDeliveryOrchestrator, DeliveryChannel
17
+ from .registry import ChannelRegistry
18
+ from .renderer import TemplateRenderer, resolve_template_path
19
+ from .threading import (
20
+ CODEX_THREAD_HEADER,
21
+ LEGACY_LILY_THREAD_HEADER,
22
+ build_message_id,
23
+ build_thread_key,
24
+ parse_references,
25
+ render_email_headers,
26
+ serialize_references,
27
+ )
28
+ from .workers_contract import (
29
+ PAYLOAD_SCHEMA_VERSION,
30
+ TASK_SEND_CAMPAIGN,
31
+ TASK_SEND_NOTIFICATION,
32
+ TASK_SEND_RENDERED,
33
+ )
34
+
35
+ __all__ = [
36
+ "AudienceBuilder",
37
+ "CampaignBatchDTO",
38
+ "CampaignDispatcher",
39
+ "CampaignRecipientDraft",
40
+ "NotificationChannel",
41
+ "NotificationPayloadDTO",
42
+ "TemplateNotificationDTO",
43
+ "RenderedNotificationDTO",
44
+ "NotificationRecipient",
45
+ "ThreadHeadersDTO",
46
+ "ContentProvider",
47
+ "ContentCacheAdapter",
48
+ "DeliveryChannel",
49
+ "AsyncEmailClient",
50
+ "BaseDeliveryOrchestrator",
51
+ "ChannelRegistry",
52
+ "TemplateRenderer",
53
+ "resolve_template_path",
54
+ "NotificationAdapter",
55
+ "ArqNotificationAdapter",
56
+ "DirectNotificationAdapter",
57
+ "CODEX_THREAD_HEADER",
58
+ "LEGACY_LILY_THREAD_HEADER",
59
+ "build_message_id",
60
+ "build_thread_key",
61
+ "parse_references",
62
+ "render_email_headers",
63
+ "serialize_references",
64
+ "PAYLOAD_SCHEMA_VERSION",
65
+ "TASK_SEND_NOTIFICATION",
66
+ "TASK_SEND_RENDERED",
67
+ "TASK_SEND_CAMPAIGN",
68
+ ]
@@ -0,0 +1,23 @@
1
+ """Audience selection contracts for campaign messaging."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable
6
+ from typing import Any, Protocol
7
+
8
+ from .campaigns.dto import CampaignRecipientDraft
9
+
10
+
11
+ class AudienceBuilder(Protocol):
12
+ """Materializes campaign recipients from a host-defined filter."""
13
+
14
+ def count(self, audience_filter: dict[str, Any]) -> int:
15
+ """Return the number of recipients matching the filter."""
16
+ ...
17
+
18
+ def materialize(self, audience_filter: dict[str, Any]) -> Iterable[CampaignRecipientDraft]:
19
+ """Stream recipient drafts matching the filter."""
20
+ ...
21
+
22
+
23
+ __all__ = ["AudienceBuilder"]
@@ -0,0 +1,6 @@
1
+ """Campaign messaging DTOs and protocols."""
2
+
3
+ from .dto import CampaignBatchDTO, CampaignRecipientDraft
4
+ from .protocols import CampaignDispatcher
5
+
6
+ __all__ = ["CampaignRecipientDraft", "CampaignBatchDTO", "CampaignDispatcher"]
@@ -0,0 +1,46 @@
1
+ """Campaign messaging DTOs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from codex_core.core import BaseDTO
8
+ from pydantic import model_validator
9
+
10
+ from codex_platform.messaging.workers_contract import PAYLOAD_SCHEMA_VERSION
11
+
12
+
13
+ class CampaignRecipientDraft(BaseDTO):
14
+ """Recipient data needed by a campaign batch worker."""
15
+
16
+ recipient_id: str
17
+ email: str
18
+ first_name: str = ""
19
+ last_name: str = ""
20
+ locale: str = "de"
21
+ unsubscribe_token: str | None = None
22
+
23
+
24
+ class CampaignBatchDTO(BaseDTO):
25
+ """One campaign batch payload for the worker callback contract."""
26
+
27
+ schema_version: int = PAYLOAD_SCHEMA_VERSION
28
+ campaign_id: str
29
+ template_name: str | None = None
30
+ html_content: str | None = None
31
+ subject: str
32
+ recipients: list[CampaignRecipientDraft]
33
+ base_context: dict[str, Any] = {}
34
+ callback_url: str
35
+ callback_token: str
36
+
37
+ @model_validator(mode="after")
38
+ def validate_rendering_mode(self) -> CampaignBatchDTO:
39
+ """Require exactly one campaign rendering mode."""
40
+
41
+ if bool(self.template_name) == bool(self.html_content):
42
+ raise ValueError("exactly one of template_name or html_content must be set")
43
+ return self
44
+
45
+
46
+ __all__ = ["CampaignRecipientDraft", "CampaignBatchDTO"]
@@ -0,0 +1,18 @@
1
+ """Campaign messaging protocols."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Protocol
6
+
7
+ from .dto import CampaignBatchDTO
8
+
9
+
10
+ class CampaignDispatcher(Protocol):
11
+ """Enqueues campaign batches for worker-side delivery."""
12
+
13
+ def enqueue_batch(self, batch: CampaignBatchDTO) -> str:
14
+ """Enqueue a batch and return the backend job identifier."""
15
+ ...
16
+
17
+
18
+ __all__ = ["CampaignDispatcher"]
@@ -1,8 +1,4 @@
1
- """
2
- codex_platform.notifications.channels
3
- =======================================
4
- Enum of supported notification delivery channels.
5
- """
1
+ """Enum of supported notification delivery channels."""
6
2
 
7
3
  from enum import StrEnum
8
4
 
@@ -1,3 +1,5 @@
1
+ """Built-in messaging delivery clients."""
2
+
1
3
  from .smtp import AsyncEmailClient
2
4
 
3
5
  __all__ = ["AsyncEmailClient"]
@@ -1,18 +1,15 @@
1
- """
2
- codex_platform.workers.notifications.email
3
- ========================================
4
- Async SMTP email client — implements ``DeliveryChannel``.
1
+ """Async SMTP email client implementing the messaging delivery channel."""
5
2
 
6
- Creates persistent SMTP connection config at init time.
7
- For multi-tenant: create separate instances with different settings.
8
-
9
- Requires: pip install codex-tools[notifications]
10
- """
3
+ from __future__ import annotations
11
4
 
12
5
  import logging
13
6
  from email.message import EmailMessage
7
+ from email.utils import formataddr
14
8
  from typing import Any
15
9
 
10
+ from codex_platform.messaging.dto import ThreadHeadersDTO
11
+ from codex_platform.messaging.threading import render_email_headers
12
+
16
13
  log = logging.getLogger(__name__)
17
14
 
18
15
  try:
@@ -21,28 +18,14 @@ try:
21
18
  _AIOSMTP_AVAILABLE = True
22
19
  except ImportError as e:
23
20
  if e.name == "aiosmtplib":
21
+ aiosmtplib = None # type: ignore[assignment]
24
22
  _AIOSMTP_AVAILABLE = False
25
23
  else:
26
24
  raise
27
25
 
28
26
 
29
27
  class AsyncEmailClient:
30
- """
31
- Async SMTP email sender. Implements ``DeliveryChannel``.
32
-
33
- Usage:
34
- client = AsyncEmailClient(
35
- smtp_host="mail.example.com", smtp_port=465,
36
- smtp_user="user", smtp_password="pass", # pragma: allowlist secret
37
- smtp_from_email="noreply@example.com",
38
- )
39
- success = await client.send(
40
- to="user@example.com",
41
- subject="Hello",
42
- html_content="<b>Hi</b>",
43
- text_content="Hi",
44
- )
45
- """
28
+ """Async SMTP email sender."""
46
29
 
47
30
  def __init__(
48
31
  self,
@@ -52,6 +35,7 @@ class AsyncEmailClient:
52
35
  smtp_password: str | None = None, # pragma: allowlist secret
53
36
  smtp_from_email: str | None = None,
54
37
  smtp_use_tls: bool = False,
38
+ smtp_from_name: str = "",
55
39
  ) -> None:
56
40
  self.smtp_host = smtp_host
57
41
  self.smtp_port = smtp_port
@@ -59,9 +43,11 @@ class AsyncEmailClient:
59
43
  self.smtp_password = smtp_password
60
44
  self.smtp_from_email = smtp_from_email
61
45
  self.smtp_use_tls = smtp_use_tls
46
+ self.smtp_from_name = smtp_from_name
62
47
 
63
48
  def is_available(self) -> bool:
64
- """Returns True if the client is configured (non-localhost host)."""
49
+ """Returns True if the client is configured with a non-localhost host."""
50
+
65
51
  return bool(self.smtp_host and self.smtp_host != "localhost")
66
52
 
67
53
  async def send(
@@ -70,30 +56,14 @@ class AsyncEmailClient:
70
56
  subject: str,
71
57
  html_content: str | None,
72
58
  text_content: str | None,
59
+ headers: ThreadHeadersDTO | None = None,
73
60
  timeout: int = 15,
74
61
  ) -> bool:
75
- """
76
- Send email via SMTP.
77
-
78
- Args:
79
- to: Recipient email address.
80
- subject: Email subject line.
81
- html_content: HTML body (preferred). Used as HTML part.
82
- text_content: Plain-text body (fallback for HTML-disabled clients).
83
- If None and html_content is provided, a generic fallback
84
- is used.
85
- timeout: SMTP connection timeout in seconds.
86
-
87
- Returns:
88
- True on success.
89
-
90
- Raises:
91
- RuntimeError: If aiosmtplib is not installed.
92
- aiosmtplib.SMTPException: On SMTP delivery failure.
93
- """
62
+ """Send email via SMTP."""
63
+
94
64
  if not _AIOSMTP_AVAILABLE:
95
- raise RuntimeError("aiosmtplib not installed. Run: pip install codex-tools[notifications]")
96
- await self._send_smtp(to, subject, html_content, text_content, timeout)
65
+ raise RuntimeError("aiosmtplib not installed. Run: pip install codex-platform[notifications]")
66
+ await self._send_smtp(to, subject, html_content, text_content, headers, timeout)
97
67
  log.info("AsyncEmailClient | sent to='%s'", to)
98
68
  return True
99
69
 
@@ -103,13 +73,22 @@ class AsyncEmailClient:
103
73
  subject: str,
104
74
  html_content: str | None,
105
75
  text_content: str | None,
76
+ headers: ThreadHeadersDTO | None,
106
77
  timeout: int,
107
78
  ) -> None:
108
79
  message = EmailMessage()
109
- message["From"] = self.smtp_from_email
80
+ message["From"] = (
81
+ formataddr((self.smtp_from_name, self.smtp_from_email))
82
+ if self.smtp_from_name and self.smtp_from_email
83
+ else self.smtp_from_email
84
+ )
110
85
  message["To"] = to
111
86
  message["Subject"] = subject
112
87
 
88
+ if headers is not None:
89
+ for name, value in render_email_headers(headers).items():
90
+ message[name] = value
91
+
113
92
  plain = text_content or "Please enable HTML to view this email."
114
93
  message.set_content(plain)
115
94
 
@@ -132,3 +111,6 @@ class AsyncEmailClient:
132
111
  send_kwargs["password"] = self.smtp_password
133
112
 
134
113
  await aiosmtplib.send(message, **send_kwargs)
114
+
115
+
116
+ __all__ = ["AsyncEmailClient", "_AIOSMTP_AVAILABLE", "aiosmtplib"]
@@ -0,0 +1,11 @@
1
+ """Delivery adapters for messaging payloads."""
2
+
3
+ from .arq import ArqNotificationAdapter
4
+ from .base import NotificationAdapter
5
+ from .direct import DirectNotificationAdapter
6
+
7
+ __all__ = [
8
+ "NotificationAdapter",
9
+ "ArqNotificationAdapter",
10
+ "DirectNotificationAdapter",
11
+ ]
@@ -0,0 +1,38 @@
1
+ """ARQ delivery adapter for messaging payloads."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import Any
7
+
8
+ from arq.connections import ArqRedis
9
+
10
+ from .base import NotificationAdapter
11
+
12
+ log = logging.getLogger(__name__)
13
+
14
+
15
+ class ArqNotificationAdapter(NotificationAdapter):
16
+ """Notification adapter that enqueues tasks into an ARQ/Redis queue."""
17
+
18
+ def __init__(self, pool: ArqRedis) -> None:
19
+ self.pool = pool
20
+
21
+ def enqueue(self, task_name: str, payload: dict[str, Any]) -> str | None:
22
+ """Sync enqueue wrapper. Prefer ``enqueue_async`` in async contexts."""
23
+
24
+ import asyncio
25
+
26
+ return asyncio.get_event_loop().run_until_complete(self.enqueue_async(task_name, payload))
27
+
28
+ async def enqueue_async(self, task_name: str, payload: dict[str, Any]) -> str | None:
29
+ """Enqueue a notification task into the ARQ queue."""
30
+
31
+ log.debug("ArqNotificationAdapter | enqueueing task=%s", task_name)
32
+ job = await self.pool.enqueue_job(task_name, payload_dict=payload)
33
+ job_id = getattr(job, "job_id", None)
34
+ log.info("ArqNotificationAdapter | task=%s job_id=%s", task_name, job_id)
35
+ return job_id
36
+
37
+
38
+ __all__ = ["ArqNotificationAdapter"]
@@ -0,0 +1,16 @@
1
+ """Protocol definition for notification delivery adapters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Protocol
6
+
7
+
8
+ class NotificationAdapter(Protocol):
9
+ """Contract for notification delivery transport."""
10
+
11
+ def enqueue(self, task_name: str, payload: dict[str, Any]) -> str | None:
12
+ """Deliver or enqueue a serialized notification payload."""
13
+ ...
14
+
15
+
16
+ __all__ = ["NotificationAdapter"]
@@ -0,0 +1,76 @@
1
+ """Direct in-process delivery adapter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import logging
7
+ from typing import Any
8
+
9
+ from .base import NotificationAdapter
10
+
11
+ log = logging.getLogger(__name__)
12
+
13
+
14
+ class DirectNotificationAdapter(NotificationAdapter):
15
+ """Adapter for synchronous in-process notification delivery."""
16
+
17
+ def __init__(self, config: Any) -> None:
18
+ self.config = config
19
+
20
+ def enqueue(self, _task_name: str, payload: dict[str, Any]) -> str | None:
21
+ """Deliver a notification synchronously via the orchestrator pipeline."""
22
+
23
+ from codex_platform.messaging.dto import (
24
+ NotificationPayloadDTO,
25
+ RenderedNotificationDTO,
26
+ TemplateNotificationDTO,
27
+ )
28
+ from codex_platform.messaging.orchestrator import BaseDeliveryOrchestrator
29
+ from codex_platform.messaging.registry import ChannelRegistry
30
+
31
+ log.debug("DirectNotificationAdapter | starting direct delivery")
32
+
33
+ registry = ChannelRegistry()
34
+ _register_default_channels(registry, self.config)
35
+ channels = registry.build_channels(self.config)
36
+ orchestrator = BaseDeliveryOrchestrator(channels=channels)
37
+
38
+ payload_dto: NotificationPayloadDTO
39
+ if "html_content" in payload:
40
+ payload_dto = RenderedNotificationDTO(**payload)
41
+ elif "template_name" in payload and "context_key" in payload:
42
+ payload_dto = TemplateNotificationDTO(**payload)
43
+ else:
44
+ payload_dto = NotificationPayloadDTO(**payload)
45
+ asyncio.run(orchestrator.deliver(payload_dto))
46
+
47
+ notification_id = payload.get("notification_id")
48
+ log.info("DirectNotificationAdapter | delivered notification_id=%s", notification_id)
49
+ return notification_id
50
+
51
+
52
+ def _register_default_channels(registry: Any, config: Any) -> None:
53
+ """Register default SMTP channel from config when available."""
54
+
55
+ try:
56
+ from codex_platform.messaging.clients.smtp import AsyncEmailClient
57
+
58
+ smtp_host = getattr(config, "SMTP_HOST", "")
59
+ if smtp_host:
60
+ registry.register(
61
+ "smtp",
62
+ lambda cfg: AsyncEmailClient(
63
+ smtp_host=getattr(cfg, "SMTP_HOST", ""),
64
+ smtp_port=getattr(cfg, "SMTP_PORT", 587),
65
+ smtp_user=getattr(cfg, "SMTP_USER", None),
66
+ smtp_password=getattr(cfg, "SMTP_PASSWORD", None),
67
+ smtp_from_email=getattr(cfg, "SMTP_FROM_EMAIL", None),
68
+ smtp_use_tls=getattr(cfg, "SMTP_USE_TLS", False),
69
+ smtp_from_name=getattr(cfg, "SMTP_FROM_NAME", ""),
70
+ ),
71
+ )
72
+ except ImportError:
73
+ pass
74
+
75
+
76
+ __all__ = ["DirectNotificationAdapter", "_register_default_channels"]