edda-framework 0.10.0__tar.gz → 0.12.0__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 (206) hide show
  1. {edda_framework-0.10.0 → edda_framework-0.12.0}/.github/workflows/ci.yml +3 -1
  2. {edda_framework-0.10.0 → edda_framework-0.12.0}/.github/workflows/docs.yml +3 -1
  3. {edda_framework-0.10.0 → edda_framework-0.12.0}/.github/workflows/release.yml +6 -2
  4. edda_framework-0.12.0/.gitmodules +3 -0
  5. {edda_framework-0.10.0 → edda_framework-0.12.0}/PKG-INFO +47 -3
  6. {edda_framework-0.10.0 → edda_framework-0.12.0}/README.md +43 -2
  7. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/core-features/durable-execution/replay.md +3 -2
  8. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/core-features/messages.md +35 -1
  9. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/getting-started/installation.md +82 -0
  10. edda_framework-0.12.0/docs/integrations/mirascope.md +373 -0
  11. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/integrations/pydantic-rpc.md +1 -1
  12. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/app.py +203 -35
  13. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/channels.py +57 -12
  14. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/context.py +24 -0
  15. edda_framework-0.12.0/edda/integrations/mirascope/__init__.py +78 -0
  16. edda_framework-0.12.0/edda/integrations/mirascope/agent.py +467 -0
  17. edda_framework-0.12.0/edda/integrations/mirascope/call.py +166 -0
  18. edda_framework-0.12.0/edda/integrations/mirascope/decorator.py +163 -0
  19. edda_framework-0.12.0/edda/integrations/mirascope/types.py +268 -0
  20. edda_framework-0.12.0/edda/migrations/mysql/20251217000000_initial_schema.sql +284 -0
  21. edda_framework-0.12.0/edda/migrations/postgresql/20251217000000_initial_schema.sql +284 -0
  22. edda_framework-0.12.0/edda/migrations/sqlite/20251217000000_initial_schema.sql +284 -0
  23. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/outbox/relayer.py +34 -7
  24. edda_framework-0.12.0/edda/storage/migrations.py +435 -0
  25. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/storage/models.py +2 -0
  26. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/storage/pg_notify.py +5 -8
  27. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/storage/sqlalchemy_storage.py +97 -61
  28. edda_framework-0.12.0/examples/mirascope/__init__.py +6 -0
  29. edda_framework-0.12.0/examples/mirascope/durable_agent.py +421 -0
  30. edda_framework-0.12.0/examples/mirascope/multi_turn.py +150 -0
  31. edda_framework-0.12.0/examples/mirascope/simple_call.py +167 -0
  32. edda_framework-0.12.0/examples/mirascope/with_tools.py +198 -0
  33. {edda_framework-0.10.0 → edda_framework-0.12.0}/pyproject.toml +16 -2
  34. edda_framework-0.12.0/schema/.dbmate.yml +16 -0
  35. edda_framework-0.12.0/schema/.git +1 -0
  36. edda_framework-0.12.0/schema/.gitignore +20 -0
  37. edda_framework-0.12.0/schema/LICENSE +21 -0
  38. edda_framework-0.12.0/schema/README.md +53 -0
  39. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/conftest.py +32 -10
  40. edda_framework-0.12.0/tests/integrations/mirascope/__init__.py +1 -0
  41. edda_framework-0.12.0/tests/integrations/mirascope/test_agent.py +403 -0
  42. edda_framework-0.12.0/tests/integrations/mirascope/test_call.py +183 -0
  43. edda_framework-0.12.0/tests/integrations/mirascope/test_decorator.py +235 -0
  44. edda_framework-0.12.0/tests/integrations/mirascope/test_types.py +449 -0
  45. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_app.py +46 -39
  46. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_auto_migration.py +6 -1
  47. edda_framework-0.12.0/tests/test_channel_direct.py +329 -0
  48. edda_framework-0.12.0/tests/test_migrations_integration.py +211 -0
  49. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_pg_notify.py +5 -5
  50. edda_framework-0.12.0/tests/test_polling_optimization.py +1026 -0
  51. {edda_framework-0.10.0 → edda_framework-0.12.0}/uv.lock +381 -49
  52. {edda_framework-0.10.0 → edda_framework-0.12.0}/zensical.toml +1 -0
  53. {edda_framework-0.10.0 → edda_framework-0.12.0}/.gitignore +0 -0
  54. {edda_framework-0.10.0 → edda_framework-0.12.0}/.python-version +0 -0
  55. {edda_framework-0.10.0 → edda_framework-0.12.0}/Justfile +0 -0
  56. {edda_framework-0.10.0 → edda_framework-0.12.0}/LICENSE +0 -0
  57. {edda_framework-0.10.0 → edda_framework-0.12.0}/demo_app.py +0 -0
  58. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/api/reference.md +0 -0
  59. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/core-features/events/cloudevents-http-binding.md +0 -0
  60. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/core-features/events/postgres-notify.md +0 -0
  61. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/core-features/events/wait-event.md +0 -0
  62. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/core-features/hooks.md +0 -0
  63. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/core-features/retry.md +0 -0
  64. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/core-features/saga-compensation.md +0 -0
  65. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/core-features/transactional-outbox.md +0 -0
  66. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/core-features/workflows-activities.md +0 -0
  67. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/examples/ecommerce.md +0 -0
  68. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/examples/events.md +0 -0
  69. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/examples/fastapi-integration.md +0 -0
  70. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/examples/saga.md +0 -0
  71. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/examples/simple.md +0 -0
  72. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/getting-started/concepts.md +0 -0
  73. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/getting-started/first-workflow.md +0 -0
  74. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/getting-started/quick-start.md +0 -0
  75. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/index.md +0 -0
  76. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/integrations/mcp.md +0 -0
  77. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/integrations/opentelemetry.md +0 -0
  78. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/viewer-ui/images/cloudevents-cli-trigger.png +0 -0
  79. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/viewer-ui/images/compensation-execution.png +0 -0
  80. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/viewer-ui/images/detail-page-loan-approval.png +0 -0
  81. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/viewer-ui/images/detail-page-match-case.png +0 -0
  82. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/viewer-ui/images/nested-pydantic-form.png +0 -0
  83. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/viewer-ui/images/start-workflow-form-pydantic.png +0 -0
  84. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/viewer-ui/images/wait-event-visualization.png +0 -0
  85. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/viewer-ui/images/workflow-list-view.png +0 -0
  86. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/viewer-ui/images/workflow-selection-dropdown.png +0 -0
  87. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/viewer-ui/setup.md +0 -0
  88. {edda_framework-0.10.0 → edda_framework-0.12.0}/docs/viewer-ui/visualization.md +0 -0
  89. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/__init__.py +0 -0
  90. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/activity.py +0 -0
  91. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/compensation.py +0 -0
  92. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/exceptions.py +0 -0
  93. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/hooks.py +0 -0
  94. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/integrations/__init__.py +0 -0
  95. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/integrations/mcp/__init__.py +0 -0
  96. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/integrations/mcp/decorators.py +0 -0
  97. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/integrations/mcp/server.py +0 -0
  98. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/integrations/opentelemetry/__init__.py +0 -0
  99. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/integrations/opentelemetry/hooks.py +0 -0
  100. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/locking.py +0 -0
  101. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/outbox/__init__.py +0 -0
  102. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/outbox/transactional.py +0 -0
  103. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/pydantic_utils.py +0 -0
  104. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/replay.py +0 -0
  105. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/retry.py +0 -0
  106. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/serialization/__init__.py +0 -0
  107. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/serialization/base.py +0 -0
  108. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/serialization/json.py +0 -0
  109. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/storage/__init__.py +0 -0
  110. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/storage/notify_base.py +0 -0
  111. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/storage/protocol.py +0 -0
  112. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/viewer_ui/__init__.py +0 -0
  113. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/viewer_ui/app.py +0 -0
  114. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/viewer_ui/components.py +0 -0
  115. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/viewer_ui/data_service.py +0 -0
  116. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/viewer_ui/theme.py +0 -0
  117. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/visualizer/__init__.py +0 -0
  118. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/visualizer/ast_analyzer.py +0 -0
  119. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/visualizer/mermaid_generator.py +0 -0
  120. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/workflow.py +0 -0
  121. {edda_framework-0.10.0 → edda_framework-0.12.0}/edda/wsgi.py +0 -0
  122. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/__init__.py +0 -0
  123. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/cancellable_workflow.py +0 -0
  124. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/compensation_workflow.py +0 -0
  125. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/event_waiting_app.py +0 -0
  126. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/event_waiting_workflow.py +0 -0
  127. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/event_waiting_workflow_complete.py +0 -0
  128. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/long_running_loop.py +0 -0
  129. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/mcp/README.md +0 -0
  130. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/mcp/order_processing_mcp.py +0 -0
  131. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/mcp/prompts_example.py +0 -0
  132. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/mcp/remote_server_example.py +0 -0
  133. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/mcp/simple_mcp_server.py +0 -0
  134. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/message_passing.py +0 -0
  135. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/observability_with_logfire.py +0 -0
  136. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/observability_with_opentelemetry.py +0 -0
  137. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/pydantic_rpc_integration.py +0 -0
  138. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/pydantic_saga.py +0 -0
  139. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/retry_example.py +0 -0
  140. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/retry_with_compensation.py +0 -0
  141. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/simple_workflow.py +0 -0
  142. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/typeddict_example.py +0 -0
  143. {edda_framework-0.10.0 → edda_framework-0.12.0}/examples/with_outbox.py +0 -0
  144. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/__init__.py +0 -0
  145. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/integrations/__init__.py +0 -0
  146. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/integrations/mcp/__init__.py +0 -0
  147. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/integrations/mcp/test_cancel.py +0 -0
  148. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/integrations/mcp/test_integration.py +0 -0
  149. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/integrations/mcp/test_jsonrpc.py +0 -0
  150. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/integrations/mcp/test_prompts.py +0 -0
  151. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/integrations/mcp/test_server.py +0 -0
  152. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/integrations/opentelemetry/__init__.py +0 -0
  153. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/integrations/opentelemetry/test_hooks.py +0 -0
  154. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_activity.py +0 -0
  155. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_activity_retry.py +0 -0
  156. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_activity_sync.py +0 -0
  157. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_ast_analyzer.py +0 -0
  158. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_atomic_wait_event.py +0 -0
  159. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_binary_data.py +0 -0
  160. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_channel_competing.py +0 -0
  161. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_channel_transactional.py +0 -0
  162. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_cloudevents_http_binding.py +0 -0
  163. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_compensation.py +0 -0
  164. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_compensation_crash_recovery.py.wip +0 -0
  165. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_concurrent_outbox.py +0 -0
  166. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_context.py +0 -0
  167. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_ctx_session.py +0 -0
  168. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_distributed_event_delivery.py +0 -0
  169. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_events.py +0 -0
  170. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_instance_id_routing.py +0 -0
  171. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_lock_race_condition.py +0 -0
  172. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_lock_timeout_customization.py +0 -0
  173. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_locking.py +0 -0
  174. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_message_cleanup.py +0 -0
  175. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_message_delivery_lock.py +0 -0
  176. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_messages.py +0 -0
  177. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_multidb_storage.py +0 -0
  178. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_outbox.py +0 -0
  179. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_pydantic_activity.py +0 -0
  180. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_pydantic_enum.py +0 -0
  181. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_pydantic_events.py +0 -0
  182. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_pydantic_saga.py +0 -0
  183. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_pydantic_utils.py +0 -0
  184. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_receive_timeout.py +0 -0
  185. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_received_event.py +0 -0
  186. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_recur.py +0 -0
  187. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_recur_cleanup.py +0 -0
  188. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_replay.py +0 -0
  189. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_retry_policy.py +0 -0
  190. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_saga_parameter_extraction.py +0 -0
  191. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_serialization.py +0 -0
  192. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_skip_locked.py +0 -0
  193. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_stale_workflow_recovery.py +0 -0
  194. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_storage.py +0 -0
  195. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_storage_mysql.py +0 -0
  196. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_storage_postgresql.py +0 -0
  197. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_transactions.py +0 -0
  198. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_viewer_pagination.py +0 -0
  199. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_viewer_pydantic_form.py +0 -0
  200. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_viewer_start_saga.py +0 -0
  201. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_wait_timer.py +0 -0
  202. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_workflow.py +0 -0
  203. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_workflow_auto_register.py +0 -0
  204. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_workflow_cancellation.py +0 -0
  205. {edda_framework-0.10.0 → edda_framework-0.12.0}/tests/test_workflow_resumption.py +0 -0
  206. {edda_framework-0.10.0 → edda_framework-0.12.0}/viewer_app.py +0 -0
@@ -45,7 +45,9 @@ jobs:
45
45
 
46
46
  steps:
47
47
  - name: Checkout code
48
- uses: actions/checkout@v4
48
+ uses: actions/checkout@v6
49
+ with:
50
+ submodules: true
49
51
 
50
52
  - name: Set up uv
51
53
  uses: astral-sh/setup-uv@v3
@@ -16,7 +16,9 @@ jobs:
16
16
  runs-on: ubuntu-latest
17
17
  steps:
18
18
  - name: Checkout code
19
- uses: actions/checkout@v4
19
+ uses: actions/checkout@v6
20
+ with:
21
+ submodules: true
20
22
 
21
23
  - name: Set up Python
22
24
  uses: actions/setup-python@v5
@@ -17,7 +17,9 @@ jobs:
17
17
 
18
18
  steps:
19
19
  - name: Checkout code
20
- uses: actions/checkout@v4
20
+ uses: actions/checkout@v6
21
+ with:
22
+ submodules: true
21
23
 
22
24
  - name: Set up uv
23
25
  uses: astral-sh/setup-uv@v3
@@ -29,7 +31,9 @@ jobs:
29
31
  run: uv sync
30
32
 
31
33
  - name: Build package
32
- run: uv build
34
+ run: |
35
+ uv build --sdist
36
+ uv build --wheel
33
37
 
34
38
  - name: Publish to PyPI
35
39
  uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,3 @@
1
+ [submodule "schema"]
2
+ path = schema
3
+ url = https://github.com/durax-io/schema.git
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: edda-framework
3
- Version: 0.10.0
3
+ Version: 0.12.0
4
4
  Summary: Lightweight Durable Execution Framework
5
5
  Project-URL: Homepage, https://github.com/i2y/edda
6
6
  Project-URL: Documentation, https://github.com/i2y/edda#readme
@@ -44,6 +44,9 @@ Requires-Dist: testcontainers[postgres]>=4.0.0; extra == 'dev'
44
44
  Requires-Dist: tsuno>=0.1.3; extra == 'dev'
45
45
  Provides-Extra: mcp
46
46
  Requires-Dist: mcp>=1.22.0; extra == 'mcp'
47
+ Provides-Extra: mirascope
48
+ Requires-Dist: mirascope[anthropic,google,openai]>=2.0.0a0; extra == 'mirascope'
49
+ Requires-Dist: pydantic-settings>=2.0.0; extra == 'mirascope'
47
50
  Provides-Extra: mysql
48
51
  Requires-Dist: aiomysql>=0.2.0; extra == 'mysql'
49
52
  Provides-Extra: opentelemetry
@@ -93,6 +96,7 @@ For detailed documentation, visit [https://i2y.github.io/edda/](https://i2y.gith
93
96
  - 📬 **Channel-based Messaging**: Actor-model style communication with competing (job queue) and broadcast (fan-out) modes
94
97
  - ⚡ **Instant Notifications**: PostgreSQL LISTEN/NOTIFY for near-instant event delivery (optional)
95
98
  - 🤖 **MCP Integration**: Expose durable workflows as AI tools via Model Context Protocol
99
+ - 🧠 **Mirascope Integration**: Durable LLM calls
96
100
  - 🌍 **ASGI/WSGI Support**: Deploy with your preferred server (uvicorn, gunicorn, uWSGI)
97
101
 
98
102
  ## Use Cases
@@ -282,6 +286,46 @@ pip install "git+https://github.com/i2y/edda.git[postgresql,viewer]"
282
286
 
283
287
  > **Tip**: For PostgreSQL, install the `postgres-notify` extra for near-instant event delivery using LISTEN/NOTIFY instead of polling.
284
288
 
289
+ ### Database Schema Migration
290
+
291
+ **Automatic Migration (Default)**
292
+
293
+ Edda automatically applies database migrations at startup. No manual commands needed:
294
+
295
+ ```python
296
+ from edda import EddaApp
297
+
298
+ # Migrations are applied automatically
299
+ app = EddaApp(db_url="postgresql://user:pass@localhost/dbname")
300
+ ```
301
+
302
+ This is safe in multi-worker environments - Edda handles concurrent startup gracefully.
303
+
304
+ **Manual Migration with dbmate (Optional)**
305
+
306
+ For explicit schema control, you can disable auto-migration and use [dbmate](https://github.com/amacneil/dbmate):
307
+
308
+ ```python
309
+ # Disable auto-migration
310
+ app = EddaApp(
311
+ db_url="postgresql://...",
312
+ auto_migrate=False # Use dbmate-managed schema
313
+ )
314
+ ```
315
+
316
+ ```bash
317
+ # Install dbmate
318
+ brew install dbmate # macOS
319
+
320
+ # Add schema submodule
321
+ git submodule add https://github.com/durax-io/schema.git schema
322
+
323
+ # Run migration manually
324
+ DATABASE_URL="postgresql://user:pass@localhost/dbname" dbmate -d ./schema/db/migrations/postgresql up
325
+ ```
326
+
327
+ > **Note**: Edda's auto-migration uses the same SQL files as dbmate, maintaining full compatibility.
328
+
285
329
  ### Development Installation
286
330
 
287
331
  If you want to contribute to Edda or modify the framework itself:
@@ -289,7 +333,7 @@ If you want to contribute to Edda or modify the framework itself:
289
333
  ```bash
290
334
  # Clone repository
291
335
  git clone https://github.com/i2y/edda.git
292
- cd kairo
336
+ cd edda
293
337
  uv sync --all-extras
294
338
  ```
295
339
 
@@ -329,7 +373,7 @@ async def user_signup(ctx: WorkflowContext, email: str):
329
373
  return {"status": "completed"}
330
374
  ```
331
375
 
332
- **Activity IDs**: Activities are automatically identified with IDs like `"send_email:1"` for deterministic replay. Manual IDs are only needed for concurrent execution (e.g., `asyncio.gather`). See [MIGRATION_GUIDE_ACTIVITY_ID.md](MIGRATION_GUIDE_ACTIVITY_ID.md) for details.
376
+ **Activity IDs**: Activities are automatically identified with IDs like `"send_email:1"` for deterministic replay. Manual IDs are only needed for concurrent execution (e.g., `asyncio.gather`).
333
377
 
334
378
  ### Durable Execution
335
379
 
@@ -31,6 +31,7 @@ For detailed documentation, visit [https://i2y.github.io/edda/](https://i2y.gith
31
31
  - 📬 **Channel-based Messaging**: Actor-model style communication with competing (job queue) and broadcast (fan-out) modes
32
32
  - ⚡ **Instant Notifications**: PostgreSQL LISTEN/NOTIFY for near-instant event delivery (optional)
33
33
  - 🤖 **MCP Integration**: Expose durable workflows as AI tools via Model Context Protocol
34
+ - 🧠 **Mirascope Integration**: Durable LLM calls
34
35
  - 🌍 **ASGI/WSGI Support**: Deploy with your preferred server (uvicorn, gunicorn, uWSGI)
35
36
 
36
37
  ## Use Cases
@@ -220,6 +221,46 @@ pip install "git+https://github.com/i2y/edda.git[postgresql,viewer]"
220
221
 
221
222
  > **Tip**: For PostgreSQL, install the `postgres-notify` extra for near-instant event delivery using LISTEN/NOTIFY instead of polling.
222
223
 
224
+ ### Database Schema Migration
225
+
226
+ **Automatic Migration (Default)**
227
+
228
+ Edda automatically applies database migrations at startup. No manual commands needed:
229
+
230
+ ```python
231
+ from edda import EddaApp
232
+
233
+ # Migrations are applied automatically
234
+ app = EddaApp(db_url="postgresql://user:pass@localhost/dbname")
235
+ ```
236
+
237
+ This is safe in multi-worker environments - Edda handles concurrent startup gracefully.
238
+
239
+ **Manual Migration with dbmate (Optional)**
240
+
241
+ For explicit schema control, you can disable auto-migration and use [dbmate](https://github.com/amacneil/dbmate):
242
+
243
+ ```python
244
+ # Disable auto-migration
245
+ app = EddaApp(
246
+ db_url="postgresql://...",
247
+ auto_migrate=False # Use dbmate-managed schema
248
+ )
249
+ ```
250
+
251
+ ```bash
252
+ # Install dbmate
253
+ brew install dbmate # macOS
254
+
255
+ # Add schema submodule
256
+ git submodule add https://github.com/durax-io/schema.git schema
257
+
258
+ # Run migration manually
259
+ DATABASE_URL="postgresql://user:pass@localhost/dbname" dbmate -d ./schema/db/migrations/postgresql up
260
+ ```
261
+
262
+ > **Note**: Edda's auto-migration uses the same SQL files as dbmate, maintaining full compatibility.
263
+
223
264
  ### Development Installation
224
265
 
225
266
  If you want to contribute to Edda or modify the framework itself:
@@ -227,7 +268,7 @@ If you want to contribute to Edda or modify the framework itself:
227
268
  ```bash
228
269
  # Clone repository
229
270
  git clone https://github.com/i2y/edda.git
230
- cd kairo
271
+ cd edda
231
272
  uv sync --all-extras
232
273
  ```
233
274
 
@@ -267,7 +308,7 @@ async def user_signup(ctx: WorkflowContext, email: str):
267
308
  return {"status": "completed"}
268
309
  ```
269
310
 
270
- **Activity IDs**: Activities are automatically identified with IDs like `"send_email:1"` for deterministic replay. Manual IDs are only needed for concurrent execution (e.g., `asyncio.gather`). See [MIGRATION_GUIDE_ACTIVITY_ID.md](MIGRATION_GUIDE_ACTIVITY_ID.md) for details.
311
+ **Activity IDs**: Activities are automatically identified with IDs like `"send_email:1"` for deterministic replay. Manual IDs are only needed for concurrent execution (e.g., `asyncio.gather`).
271
312
 
272
313
  ### Durable Execution
273
314
 
@@ -349,7 +349,7 @@ async def resume_workflow_endpoint(instance_id: str):
349
349
 
350
350
  Edda automatically recovers from crashes in two stages:
351
351
 
352
- #### 3-1. Stale Lock Cleanup (Implemented)
352
+ #### 3-1. Stale Lock Cleanup
353
353
 
354
354
  When a worker process crashes, its locks become "stale." Edda automatically cleans these up:
355
355
 
@@ -394,7 +394,7 @@ This background task starts automatically when `EddaApp` launches.
394
394
 
395
395
  The `status` field indicates whether the workflow was running normally (`"running"`) or executing compensations (`"compensating"`) when it crashed.
396
396
 
397
- #### 3-2. Automatic Workflow Resume (Implemented)
397
+ #### 3-2. Automatic Workflow Resume
398
398
 
399
399
  After cleaning stale locks, Edda automatically resumes workflows with `status="running"` or `status="compensating"`:
400
400
 
@@ -586,6 +586,7 @@ async with workflow_lock(storage, instance_id, worker_id, timeout_seconds=300):
586
586
  ```
587
587
 
588
588
  Features:
589
+
589
590
  - **5-minute timeout** by default (prevents indefinite locks)
590
591
  - **Worker ID tracking** (know which worker holds the lock)
591
592
  - **Stale lock cleanup** (automatic recovery after crashes)
@@ -47,6 +47,40 @@ async def notification_service(ctx: WorkflowContext, service_id: str):
47
47
 
48
48
  - `"broadcast"` (default): All subscribers receive all messages. Use for fan-out patterns like notifications.
49
49
  - `"competing"`: Each message is processed by only one subscriber. Use for job queues and task distribution.
50
+ - `"direct"`: Receive messages sent via `send_to()` to this specific instance. Syntactic sugar for point-to-point messaging.
51
+
52
+ **Using `mode="direct"`**:
53
+
54
+ The `"direct"` mode simplifies receiving messages sent via `send_to()`:
55
+
56
+ ```python
57
+ @workflow
58
+ async def direct_receiver(ctx: WorkflowContext, id: str):
59
+ # Subscribe to receive direct messages
60
+ await subscribe(ctx, "notifications", mode="direct")
61
+
62
+ # Wait for a message sent via send_to()
63
+ msg = await receive(ctx, "notifications")
64
+ return msg.data
65
+
66
+ @workflow
67
+ async def sender(ctx: WorkflowContext, receiver_id: str):
68
+ # Send directly to the receiver instance
69
+ await send_to(ctx, instance_id=receiver_id, data={"hello": "world"}, channel="notifications")
70
+ ```
71
+
72
+ This is equivalent to manually constructing the channel name:
73
+
74
+ ```python
75
+ # Without mode="direct" (manual approach)
76
+ direct_channel = f"notifications:{ctx.instance_id}"
77
+ await subscribe(ctx, direct_channel, mode="broadcast")
78
+ msg = await receive(ctx, direct_channel)
79
+
80
+ # With mode="direct" (simplified)
81
+ await subscribe(ctx, "notifications", mode="direct")
82
+ msg = await receive(ctx, "notifications")
83
+ ```
50
84
 
51
85
  #### `unsubscribe()`
52
86
 
@@ -202,7 +236,7 @@ async def subscribe(
202
236
 
203
237
  - `ctx`: Workflow context
204
238
  - `channel`: Channel name to subscribe to
205
- - `mode`: `"broadcast"` (all subscribers receive) or `"competing"` (one subscriber per message)
239
+ - `mode`: `"broadcast"` (all subscribers receive), `"competing"` (one subscriber per message), or `"direct"` (receive messages from `send_to()`)
206
240
 
207
241
  ### receive()
208
242
 
@@ -310,6 +310,88 @@ app = EddaApp(
310
310
  )
311
311
  ```
312
312
 
313
+ ### Schema Migration
314
+
315
+ #### Automatic Migration (Default)
316
+
317
+ Edda automatically applies database migrations at startup. No manual commands needed:
318
+
319
+ ```python
320
+ from edda import EddaApp
321
+
322
+ # Migrations are applied automatically at startup
323
+ app = EddaApp(
324
+ service_name="demo-service",
325
+ db_url="postgresql://user:pass@localhost/dbname"
326
+ )
327
+ ```
328
+
329
+ **Key features:**
330
+
331
+ - **Zero configuration**: Works out of the box
332
+ - **Multi-worker safe**: Handles concurrent startup gracefully (race condition protected)
333
+ - **dbmate compatible**: Uses the same SQL files and `schema_migrations` table
334
+ - **Incremental**: Only applies pending migrations
335
+
336
+ #### Manual Migration with dbmate (Optional)
337
+
338
+ For explicit schema control, you can disable auto-migration and use [dbmate](https://github.com/amacneil/dbmate):
339
+
340
+ ```python
341
+ # Disable auto-migration
342
+ app = EddaApp(
343
+ service_name="demo-service",
344
+ db_url="postgresql://...",
345
+ auto_migrate=False # Use dbmate-managed schema
346
+ )
347
+ ```
348
+
349
+ ```bash
350
+ # Install dbmate
351
+ brew install dbmate # macOS
352
+ # Linux: curl -fsSL https://github.com/amacneil/dbmate/releases/latest/download/dbmate-linux-amd64 -o /usr/local/bin/dbmate && chmod +x /usr/local/bin/dbmate
353
+
354
+ # Add schema submodule to your project
355
+ git submodule add https://github.com/durax-io/schema.git schema
356
+
357
+ # Run migration manually
358
+ DATABASE_URL="postgresql://user:pass@localhost/dbname" dbmate -d ./schema/db/migrations/postgresql up
359
+
360
+ # Check status
361
+ dbmate -d ./schema/db/migrations/postgresql status
362
+ ```
363
+
364
+ > **Note**: Edda's auto-migration uses the same SQL files as dbmate, so you can switch between modes freely.
365
+
366
+ ### Multi-Worker Configuration
367
+
368
+ When running multiple Edda workers (e.g., in Kubernetes or with multiple processes), Edda automatically coordinates background tasks using **leader election**. Only one worker runs maintenance tasks (timers, message cleanup, etc.) while others focus on workflow execution.
369
+
370
+ ```python
371
+ from edda import EddaApp
372
+
373
+ app = EddaApp(
374
+ service_name="demo-service",
375
+ db_url="postgresql://...",
376
+ # Leader election settings (optional - defaults work well for most cases)
377
+ leader_heartbeat_interval=15, # How often workers check/renew leadership
378
+ leader_lease_duration=45, # How long before a failed leader is replaced
379
+ )
380
+ ```
381
+
382
+ **Configuration options:**
383
+
384
+ | Parameter | Type | Default | Description |
385
+ |-----------|------|---------|-------------|
386
+ | `leader_heartbeat_interval` | `int` | `15` | Interval in seconds for leader heartbeat |
387
+ | `leader_lease_duration` | `int` | `45` | Duration in seconds before leadership expires |
388
+
389
+ **Notes:**
390
+
391
+ - Default values work well for most deployments
392
+ - Reduce `leader_lease_duration` for faster failover (minimum: 3x heartbeat interval)
393
+ - Leader election uses the database for coordination (no external dependencies)
394
+
313
395
  ## Next Steps
314
396
 
315
397
  - **[Quick Start](quick-start.md)**: Build your first workflow in 5 minutes