mageflow 0.3.0__tar.gz → 0.3.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.
- {mageflow-0.3.0 → mageflow-0.3.2}/PKG-INFO +3 -2
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/__init__.py +13 -6
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/chain/workflows.py +1 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/client.py +22 -4
- mageflow-0.3.2/mageflow/clients/__init__.py +3 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/clients/hatchet/adapter.py +11 -12
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/clients/hatchet/mageflow.py +34 -24
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/clients/hatchet/workflow.py +1 -0
- mageflow-0.3.2/mageflow/config.py +47 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/lifecycle/signature.py +2 -1
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/lifecycle/task.py +1 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/startup.py +19 -3
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/swarm/workflows.py +2 -1
- mageflow-0.3.2/mageflow/utils/mageflow.py +2 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/pyproject.toml +3 -2
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/conftest.py +8 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/assertions.py +5 -5
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/chain/test_stop_resume.py +4 -5
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/conftest.py +5 -16
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/models.py +10 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/swarm/test__swarm.py +8 -8
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/swarm/test_edge_cases.py +5 -5
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/swarm/test_stop_resume.py +5 -5
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/test_task_models.py +2 -2
- mageflow-0.3.2/tests/integration/hatchet/test_ttl.py +64 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/worker.py +59 -5
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/test_redis_ttl.py +12 -8
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/assertions.py +7 -1
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/callbacks/conftest.py +5 -5
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/callbacks/test_handle_task_callback.py +3 -3
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/clients/test_hatchet_adapter.py +7 -7
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/conftest.py +9 -9
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/idempotency/conftest.py +6 -6
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/idempotency/test_chain_workflows_idempotent.py +4 -4
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/idempotency/test_fill_running_tasks_idempotent.py +3 -3
- mageflow-0.3.2/tests/unit/race_condition/test_fill_running_tasks.py +54 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/test_client.py +2 -3
- mageflow-0.3.2/tests/unit/test_param_config.py +110 -0
- mageflow-0.3.2/tests/unit/test_remove_ttl.py +63 -0
- mageflow-0.3.2/tests/unit/test_ttl_config.py +68 -0
- mageflow-0.3.2/tests/unit/utils.py +8 -0
- mageflow-0.3.2/tests/unit/workflows/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/workflows/conftest.py +4 -4
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/workflows/test_swarm_item_failed.py +2 -2
- mageflow-0.3.0/mageflow/clients/__init__.py +0 -7
- mageflow-0.3.0/mageflow/utils/mageflow.py +0 -2
- mageflow-0.3.0/uv.lock +0 -1508
- {mageflow-0.3.0 → mageflow-0.3.2}/.gitignore +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/callbacks.py +2 -2
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/chain/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/chain/messages.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/clients/hatchet/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/clients/inner_task_names.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/lifecycle/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/swarm/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/swarm/consts.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/swarm/messages.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/utils/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/mageflow/utils/pythonic.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/chain/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/chain/test__chain.py +6 -6
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/chain/test_edge_cases.py +3 -3
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/docker-compose.hatchet.yml +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/signature/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/signature/test__signature.py +10 -10
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/signature/test_edge_case.py +9 -9
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/signature/test_stop_resume.py +6 -6
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/swarm/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/swarm/test_workflow.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/integration/hatchet/test_complex_scenarios.py +10 -10
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/callbacks/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/clients/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/idempotency/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/idempotency/test_fill_workflow_running_tasks_idempotent.py +3 -3
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/idempotency/test_swarm_item_done_idempotent.py +1 -1
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/idempotency/test_swarm_item_failed_idempotent.py +1 -1
- {mageflow-0.3.0/tests/unit/workflows → mageflow-0.3.2/tests/unit/race_condition}/__init__.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/test_client_signature_compatibility.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/workflows/test_chain_end.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/workflows/test_chain_error.py +0 -0
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/workflows/test_fill_running_tasks.py +3 -3
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/workflows/test_fill_swarm_corrupted_callbacks.py +1 -1
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/workflows/test_fill_swarm_running_tasks.py +2 -2
- {mageflow-0.3.0 → mageflow-0.3.2}/tests/unit/workflows/test_swarm_item_done.py +1 -1
- {mageflow-0.3.0 → mageflow-0.3.2}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mageflow
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Manage Graph Execution Flow - A unified interface for task orchestration across different task managers
|
|
5
5
|
Project-URL: Homepage, https://imaginary-cherry.github.io/mageflow/
|
|
6
6
|
Project-URL: Documentation, https://imaginary-cherry.github.io/mageflow/
|
|
@@ -26,7 +26,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
26
26
|
Classifier: Topic :: System :: Distributed Computing
|
|
27
27
|
Classifier: Typing :: Typed
|
|
28
28
|
Requires-Python: <3.14,>=3.10
|
|
29
|
-
Requires-Dist: thirdmagic
|
|
29
|
+
Requires-Dist: thirdmagic<0.1.0,>=0.0.3
|
|
30
30
|
Provides-Extra: dev
|
|
31
31
|
Requires-Dist: black>=26.1.0; extra == 'dev'
|
|
32
32
|
Requires-Dist: coverage[toml]<8.0.0,>=7.0.0; extra == 'dev'
|
|
@@ -36,5 +36,6 @@ Requires-Dist: psutil<8.0.0,>=7.1.3; extra == 'dev'
|
|
|
36
36
|
Requires-Dist: pytest-asyncio<2.0.0,>=1.2.0; extra == 'dev'
|
|
37
37
|
Requires-Dist: pytest<10.0.0,>=9.0.2; extra == 'dev'
|
|
38
38
|
Requires-Dist: requests<3.0.0,>=2.32.5; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.15.5; extra == 'dev'
|
|
39
40
|
Provides-Extra: hatchet
|
|
40
41
|
Requires-Dist: hatchet-sdk<1.24.0,>=1.22.5; extra == 'hatchet'
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import rapyer
|
|
2
2
|
from rapyer.fields import RapyerKey
|
|
3
|
-
from thirdmagic.chain.creator import chain as achain
|
|
4
|
-
from thirdmagic.signature import Signature
|
|
5
|
-
from thirdmagic.task import sign as asign, TaskSignature
|
|
6
|
-
from thirdmagic.swarm.creator import swarm as aswarm
|
|
7
3
|
|
|
8
4
|
from mageflow.callbacks import handle_task_callback
|
|
9
5
|
from mageflow.client import Mageflow
|
|
6
|
+
from mageflow.config import MageflowConfig, SignatureTTLConfig, TTLConfig
|
|
7
|
+
from mageflow.startup import start_mageflow
|
|
8
|
+
from thirdmagic.chain.creator import chain as achain
|
|
9
|
+
from thirdmagic.signature import Signature
|
|
10
|
+
from thirdmagic.swarm.creator import swarm as aswarm
|
|
11
|
+
from thirdmagic.task import TaskSignature
|
|
12
|
+
from thirdmagic.task import sign as asign
|
|
10
13
|
|
|
11
|
-
resume_task = TaskSignature.resume_from_key
|
|
12
14
|
lock_task = TaskSignature.alock_from_key
|
|
13
15
|
resume = TaskSignature.resume_from_key
|
|
14
16
|
pause = TaskSignature.pause_from_key
|
|
@@ -20,11 +22,11 @@ async def load_sign(key: RapyerKey) -> Signature:
|
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
load_signature = rapyer.afind_one
|
|
25
|
+
abounded_field = rapyer.apipeline
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
__all__ = [
|
|
26
29
|
"load_signature",
|
|
27
|
-
"resume_task",
|
|
28
30
|
"lock_task",
|
|
29
31
|
"resume",
|
|
30
32
|
"remove",
|
|
@@ -32,6 +34,11 @@ __all__ = [
|
|
|
32
34
|
"asign",
|
|
33
35
|
"handle_task_callback",
|
|
34
36
|
"Mageflow",
|
|
37
|
+
"MageflowConfig",
|
|
38
|
+
"TTLConfig",
|
|
39
|
+
"SignatureTTLConfig",
|
|
35
40
|
"achain",
|
|
36
41
|
"aswarm",
|
|
42
|
+
"start_mageflow",
|
|
43
|
+
"abounded_field",
|
|
37
44
|
]
|
|
@@ -1,29 +1,47 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import warnings
|
|
2
3
|
from typing import TypeVar, overload
|
|
3
4
|
|
|
4
5
|
import redis
|
|
5
6
|
from hatchet_sdk import Hatchet
|
|
6
7
|
from redis.asyncio import Redis
|
|
7
|
-
from thirdmagic.signature import Signature
|
|
8
8
|
|
|
9
9
|
from mageflow.callbacks import AcceptParams
|
|
10
10
|
from mageflow.clients.hatchet.adapter import HatchetClientAdapter
|
|
11
11
|
from mageflow.clients.hatchet.mageflow import HatchetMageflow
|
|
12
|
+
from mageflow.config import MageflowConfig
|
|
13
|
+
from thirdmagic.signature import Signature
|
|
12
14
|
|
|
13
15
|
T = TypeVar("T")
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
@overload
|
|
17
19
|
def Mageflow(
|
|
18
|
-
hatchet_client: Hatchet,
|
|
20
|
+
hatchet_client: Hatchet,
|
|
21
|
+
redis_client: Redis | str = None,
|
|
22
|
+
param_config: AcceptParams = None,
|
|
23
|
+
config: MageflowConfig = None,
|
|
19
24
|
) -> HatchetMageflow: ...
|
|
20
25
|
|
|
21
26
|
|
|
22
27
|
def Mageflow(
|
|
23
28
|
hatchet_client: T = None,
|
|
24
29
|
redis_client: Redis | str = None,
|
|
25
|
-
param_config: AcceptParams =
|
|
30
|
+
param_config: AcceptParams = None,
|
|
31
|
+
config: MageflowConfig = None,
|
|
26
32
|
) -> T:
|
|
33
|
+
if config is None:
|
|
34
|
+
config = MageflowConfig()
|
|
35
|
+
|
|
36
|
+
if param_config is not None:
|
|
37
|
+
warnings.warn(
|
|
38
|
+
"Passing 'param_config' directly to Mageflow() is deprecated. "
|
|
39
|
+
"Set it via MageflowConfig(param_config=...) instead.",
|
|
40
|
+
DeprecationWarning,
|
|
41
|
+
stacklevel=2,
|
|
42
|
+
)
|
|
43
|
+
config.param_config = param_config
|
|
44
|
+
|
|
27
45
|
if hatchet_client is None:
|
|
28
46
|
hatchet_client = Hatchet()
|
|
29
47
|
|
|
@@ -35,4 +53,4 @@ def Mageflow(
|
|
|
35
53
|
redis_client = redis.asyncio.from_url(redis_url, decode_responses=True)
|
|
36
54
|
if isinstance(redis_client, str):
|
|
37
55
|
redis_client = redis.asyncio.from_url(redis_client, decode_responses=True)
|
|
38
|
-
return HatchetMageflow(hatchet_client, redis_client,
|
|
56
|
+
return HatchetMageflow(hatchet_client, redis_client, config)
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
from typing import Any, cast
|
|
2
2
|
|
|
3
3
|
import rapyer
|
|
4
|
-
from hatchet_sdk import Hatchet, NonRetryableException
|
|
4
|
+
from hatchet_sdk import Context, Hatchet, NonRetryableException
|
|
5
5
|
from hatchet_sdk.clients.admin import TriggerWorkflowOptions
|
|
6
6
|
from hatchet_sdk.runnables.contextvars import ctx_additional_metadata
|
|
7
7
|
from hatchet_sdk.runnables.types import EmptyModel
|
|
8
8
|
from hatchet_sdk.runnables.workflow import BaseWorkflow
|
|
9
9
|
from pydantic import BaseModel, TypeAdapter
|
|
10
10
|
from rapyer.fields import RapyerKey
|
|
11
|
-
from thirdmagic.chain import ChainTaskSignature
|
|
12
|
-
from thirdmagic.clients.base import BaseClientAdapter
|
|
13
|
-
from thirdmagic.consts import TASK_ID_PARAM_NAME
|
|
14
|
-
from thirdmagic.signature import Signature
|
|
15
|
-
from thirdmagic.swarm import SwarmTaskSignature
|
|
16
|
-
from thirdmagic.task import TaskSignature
|
|
17
|
-
from thirdmagic.task_def import MageflowTaskDefinition
|
|
18
11
|
|
|
19
12
|
from mageflow.chain.messages import ChainCallbackMessage, ChainErrorMessage
|
|
20
13
|
from mageflow.clients.hatchet.workflow import MageflowWorkflow
|
|
@@ -23,16 +16,22 @@ from mageflow.clients.inner_task_names import (
|
|
|
23
16
|
ON_CHAIN_ERROR,
|
|
24
17
|
ON_SWARM_ITEM_DONE,
|
|
25
18
|
ON_SWARM_ITEM_ERROR,
|
|
19
|
+
SWARM_FILL_TASK,
|
|
26
20
|
)
|
|
27
|
-
from mageflow.clients.inner_task_names import SWARM_FILL_TASK
|
|
28
21
|
from mageflow.lifecycle.signature import SignatureLifecycle
|
|
29
22
|
from mageflow.lifecycle.task import TaskLifecycle
|
|
30
23
|
from mageflow.swarm.messages import (
|
|
31
|
-
SwarmMessage,
|
|
32
|
-
SwarmResultsMessage,
|
|
33
|
-
SwarmErrorMessage,
|
|
34
24
|
FillSwarmMessage,
|
|
25
|
+
SwarmErrorMessage,
|
|
26
|
+
SwarmResultsMessage,
|
|
35
27
|
)
|
|
28
|
+
from thirdmagic.chain import ChainTaskSignature
|
|
29
|
+
from thirdmagic.clients.base import BaseClientAdapter
|
|
30
|
+
from thirdmagic.consts import TASK_ID_PARAM_NAME
|
|
31
|
+
from thirdmagic.signature import Signature
|
|
32
|
+
from thirdmagic.swarm import SwarmTaskSignature
|
|
33
|
+
from thirdmagic.task import TaskSignature
|
|
34
|
+
from thirdmagic.task_def import MageflowTaskDefinition
|
|
36
35
|
|
|
37
36
|
|
|
38
37
|
class HatchetClientAdapter(BaseClientAdapter):
|
|
@@ -3,43 +3,36 @@ import functools
|
|
|
3
3
|
import inspect
|
|
4
4
|
import random
|
|
5
5
|
from datetime import timedelta
|
|
6
|
-
from typing import Any,
|
|
6
|
+
from typing import Any, Callable, TypedDict, Unpack
|
|
7
7
|
|
|
8
|
-
from hatchet_sdk import Hatchet, Worker
|
|
8
|
+
from hatchet_sdk import Context, Hatchet, Worker
|
|
9
9
|
from hatchet_sdk.labels import DesiredWorkerLabel
|
|
10
10
|
from hatchet_sdk.rate_limit import RateLimit
|
|
11
11
|
from hatchet_sdk.runnables.types import (
|
|
12
|
-
StickyStrategy,
|
|
13
12
|
ConcurrencyExpression,
|
|
14
|
-
DefaultFilter,
|
|
15
13
|
ConcurrencyLimitStrategy,
|
|
14
|
+
DefaultFilter,
|
|
15
|
+
StickyStrategy,
|
|
16
16
|
)
|
|
17
17
|
from hatchet_sdk.runnables.workflow import BaseWorkflow, Standalone
|
|
18
18
|
from hatchet_sdk.worker.worker import LifespanFn
|
|
19
19
|
from redis.asyncio import Redis
|
|
20
|
-
from thirdmagic import sign, chain
|
|
21
|
-
from thirdmagic.chain import ChainTaskSignature
|
|
22
|
-
from thirdmagic.signature import Signature
|
|
23
|
-
from thirdmagic.swarm import SwarmTaskSignature
|
|
24
|
-
from thirdmagic.swarm.creator import SignatureOptions, swarm
|
|
25
|
-
from thirdmagic.task import TaskSignatureConvertible, TaskSignature, TaskInputType
|
|
26
|
-
from thirdmagic.task_def import MageflowTaskDefinition
|
|
27
|
-
from thirdmagic.utils import HatchetTaskType
|
|
28
20
|
from typing_extensions import override
|
|
29
21
|
|
|
30
22
|
from mageflow.callbacks import AcceptParams, handle_task_callback
|
|
31
23
|
from mageflow.chain.messages import ChainCallbackMessage, ChainErrorMessage
|
|
32
24
|
from mageflow.chain.workflows import chain_end_task, chain_error_task
|
|
33
25
|
from mageflow.clients.inner_task_names import (
|
|
34
|
-
ON_CHAIN_ERROR,
|
|
35
26
|
ON_CHAIN_END,
|
|
36
|
-
|
|
37
|
-
ON_SWARM_ITEM_ERROR,
|
|
27
|
+
ON_CHAIN_ERROR,
|
|
38
28
|
ON_SWARM_ITEM_DONE,
|
|
29
|
+
ON_SWARM_ITEM_ERROR,
|
|
30
|
+
SWARM_FILL_TASK,
|
|
39
31
|
)
|
|
32
|
+
from mageflow.config import MageflowConfig
|
|
40
33
|
from mageflow.startup import (
|
|
41
|
-
lifespan_initialize,
|
|
42
34
|
init_mageflow,
|
|
35
|
+
lifespan_initialize,
|
|
43
36
|
teardown_mageflow,
|
|
44
37
|
)
|
|
45
38
|
from mageflow.swarm.consts import SWARM_TASK_ID_PARAM_NAME
|
|
@@ -50,10 +43,18 @@ from mageflow.swarm.messages import (
|
|
|
50
43
|
)
|
|
51
44
|
from mageflow.swarm.workflows import (
|
|
52
45
|
fill_swarm_running_tasks,
|
|
53
|
-
swarm_item_failed,
|
|
54
46
|
swarm_item_done,
|
|
47
|
+
swarm_item_failed,
|
|
55
48
|
)
|
|
56
49
|
from mageflow.utils.mageflow import does_task_wants_ctx
|
|
50
|
+
from thirdmagic import chain, sign
|
|
51
|
+
from thirdmagic.chain import ChainTaskSignature
|
|
52
|
+
from thirdmagic.signature import Signature
|
|
53
|
+
from thirdmagic.swarm import SwarmTaskSignature
|
|
54
|
+
from thirdmagic.swarm.creator import SignatureOptions, swarm
|
|
55
|
+
from thirdmagic.task import TaskInputType, TaskSignature, TaskSignatureConvertible
|
|
56
|
+
from thirdmagic.task_def import MageflowTaskDefinition
|
|
57
|
+
from thirdmagic.utils import HatchetTaskType
|
|
57
58
|
|
|
58
59
|
Duration = timedelta | str
|
|
59
60
|
|
|
@@ -86,9 +87,12 @@ class WorkerOptions(TypedDict, total=False):
|
|
|
86
87
|
|
|
87
88
|
|
|
88
89
|
async def merge_lifespan(
|
|
89
|
-
redis: Redis,
|
|
90
|
+
redis: Redis,
|
|
91
|
+
tasks: list[MageflowTaskDefinition],
|
|
92
|
+
config: MageflowConfig,
|
|
93
|
+
original_lifespan: LifespanFn,
|
|
90
94
|
):
|
|
91
|
-
await init_mageflow(redis, tasks)
|
|
95
|
+
await init_mageflow(redis, tasks, config)
|
|
92
96
|
async for res in original_lifespan():
|
|
93
97
|
yield res
|
|
94
98
|
await teardown_mageflow()
|
|
@@ -99,12 +103,12 @@ class HatchetMageflow(Hatchet):
|
|
|
99
103
|
self,
|
|
100
104
|
hatchet: Hatchet,
|
|
101
105
|
redis_client: Redis,
|
|
102
|
-
|
|
106
|
+
config: MageflowConfig = None,
|
|
103
107
|
):
|
|
104
108
|
super().__init__(client=hatchet._client)
|
|
105
109
|
self.hatchet = hatchet
|
|
106
110
|
self.redis = redis_client
|
|
107
|
-
self.
|
|
111
|
+
self.mageflow_config = config or MageflowConfig()
|
|
108
112
|
self._task_defs: list[MageflowTaskDefinition] = []
|
|
109
113
|
|
|
110
114
|
@property
|
|
@@ -123,7 +127,9 @@ class HatchetMageflow(Hatchet):
|
|
|
123
127
|
|
|
124
128
|
def task_decorator(self, func: Callable, hatchet_task):
|
|
125
129
|
param_config = (
|
|
126
|
-
AcceptParams.ALL
|
|
130
|
+
AcceptParams.ALL
|
|
131
|
+
if does_task_wants_ctx(func)
|
|
132
|
+
else self.mageflow_config.param_config
|
|
127
133
|
)
|
|
128
134
|
send_signature = getattr(func, "__send_signature__", False)
|
|
129
135
|
handler_dec = handle_task_callback(param_config, send_signature=send_signature)
|
|
@@ -227,11 +233,15 @@ class HatchetMageflow(Hatchet):
|
|
|
227
233
|
workflows += mageflow_flows
|
|
228
234
|
if lifespan is None:
|
|
229
235
|
lifespan = functools.partial(
|
|
230
|
-
lifespan_initialize, self.redis, self._task_defs
|
|
236
|
+
lifespan_initialize, self.redis, self._task_defs, self.mageflow_config
|
|
231
237
|
)
|
|
232
238
|
else:
|
|
233
239
|
lifespan = functools.partial(
|
|
234
|
-
merge_lifespan,
|
|
240
|
+
merge_lifespan,
|
|
241
|
+
self.redis,
|
|
242
|
+
self._task_defs,
|
|
243
|
+
self.mageflow_config,
|
|
244
|
+
lifespan,
|
|
235
245
|
)
|
|
236
246
|
|
|
237
247
|
return super().worker(name, workflows=workflows, lifespan=lifespan, **kwargs)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from mageflow.callbacks import AcceptParams
|
|
6
|
+
from thirdmagic.chain import ChainTaskSignature
|
|
7
|
+
from thirdmagic.consts import REMOVED_TASK_TTL
|
|
8
|
+
from thirdmagic.signature import SignatureConfig
|
|
9
|
+
from thirdmagic.swarm import SwarmTaskSignature
|
|
10
|
+
from thirdmagic.swarm.state import PublishState
|
|
11
|
+
from thirdmagic.task import TaskSignature
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class SignatureTTLConfig:
|
|
16
|
+
active_ttl: Optional[int] = None # seconds, None = use general
|
|
17
|
+
ttl_when_sign_done: Optional[int] = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class TTLConfig:
|
|
22
|
+
active_ttl: int = 24 * 60 * 60 # general active TTL (default 24h)
|
|
23
|
+
ttl_when_sign_done: int = REMOVED_TASK_TTL # general done TTL (default 5min)
|
|
24
|
+
task: SignatureTTLConfig = field(default_factory=SignatureTTLConfig)
|
|
25
|
+
chain: SignatureTTLConfig = field(default_factory=SignatureTTLConfig)
|
|
26
|
+
swarm: SignatureTTLConfig = field(default_factory=SignatureTTLConfig)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class MageflowConfig:
|
|
31
|
+
ttl: TTLConfig = field(default_factory=TTLConfig)
|
|
32
|
+
param_config: AcceptParams = AcceptParams.NO_CTX
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def apply_ttl_config(ttl_config: TTLConfig):
|
|
36
|
+
config_mapping = {
|
|
37
|
+
TaskSignature: ttl_config.task,
|
|
38
|
+
ChainTaskSignature: ttl_config.chain,
|
|
39
|
+
SwarmTaskSignature: ttl_config.swarm,
|
|
40
|
+
PublishState: ttl_config.swarm,
|
|
41
|
+
}
|
|
42
|
+
for sig_type, sig_config in config_mapping.items():
|
|
43
|
+
active_ttl = sig_config.active_ttl or ttl_config.active_ttl
|
|
44
|
+
done_ttl = sig_config.ttl_when_sign_done or ttl_config.ttl_when_sign_done
|
|
45
|
+
|
|
46
|
+
sig_type.Meta = dataclasses.replace(sig_type.Meta, ttl=active_ttl)
|
|
47
|
+
sig_type.SignatureSettings = SignatureConfig(ttl_when_sign_done=done_ttl)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Any, Optional, cast
|
|
3
3
|
|
|
4
4
|
import rapyer
|
|
5
|
+
|
|
5
6
|
from thirdmagic.clients.lifecycle import BaseLifecycle
|
|
6
7
|
from thirdmagic.container import ContainerTaskSignature
|
|
7
8
|
from thirdmagic.signature import Signature
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import rapyer
|
|
2
2
|
from redis.asyncio import Redis
|
|
3
|
+
|
|
4
|
+
from mageflow.config import MageflowConfig, apply_ttl_config
|
|
3
5
|
from thirdmagic.task_def import MageflowTaskDefinition
|
|
4
6
|
|
|
5
7
|
|
|
6
|
-
async def
|
|
8
|
+
async def start_mageflow(redis: Redis, config: MageflowConfig = None):
|
|
9
|
+
await init_mageflow(redis, [], config)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def init_mageflow(
|
|
13
|
+
redis: Redis,
|
|
14
|
+
tasks: list[MageflowTaskDefinition],
|
|
15
|
+
config: MageflowConfig = None,
|
|
16
|
+
):
|
|
17
|
+
if config is not None:
|
|
18
|
+
apply_ttl_config(config.ttl)
|
|
7
19
|
# Init redis in local async loop
|
|
8
20
|
redis = Redis(**redis.connection_pool.connection_kwargs)
|
|
9
21
|
await rapyer.init_rapyer(redis, prefer_normal_json_dump=True)
|
|
@@ -18,10 +30,14 @@ async def register_workflows(tasks: list[MageflowTaskDefinition]):
|
|
|
18
30
|
await MageflowTaskDefinition.ainsert(*tasks)
|
|
19
31
|
|
|
20
32
|
|
|
21
|
-
async def lifespan_initialize(
|
|
33
|
+
async def lifespan_initialize(
|
|
34
|
+
redis: Redis,
|
|
35
|
+
tasks: list[MageflowTaskDefinition],
|
|
36
|
+
config: MageflowConfig = None,
|
|
37
|
+
):
|
|
22
38
|
# Init redis in local async loop
|
|
23
39
|
redis = Redis(**redis.connection_pool.connection_kwargs)
|
|
24
|
-
await init_mageflow(redis, tasks)
|
|
40
|
+
await init_mageflow(redis, tasks, config)
|
|
25
41
|
# yield makes the function usable as a Hatchet lifespan context manager (can also be used for FastAPI):
|
|
26
42
|
# - code before yield runs at startup (init config, register workers, etc.)
|
|
27
43
|
# - code after yield would run at shutdown
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from logging import Logger
|
|
2
|
-
from typing import Optional, cast
|
|
2
|
+
from typing import Any, Optional, cast
|
|
3
3
|
|
|
4
4
|
import rapyer
|
|
5
5
|
from rapyer.fields import RapyerKey
|
|
6
|
+
|
|
6
7
|
from thirdmagic.clients.lifecycle import BaseLifecycle
|
|
7
8
|
from thirdmagic.signature import Signature
|
|
8
9
|
from thirdmagic.swarm import PublishState
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mageflow"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.2"
|
|
4
4
|
description = "Manage Graph Execution Flow - A unified interface for task orchestration across different task managers"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "imaginary-cherry", email = "yedidyakfir@gmail.com"}
|
|
@@ -41,7 +41,7 @@ classifiers = [
|
|
|
41
41
|
"Operating System :: OS Independent",
|
|
42
42
|
]
|
|
43
43
|
dependencies = [
|
|
44
|
-
"thirdmagic",
|
|
44
|
+
"thirdmagic>=0.0.3, <0.1.0",
|
|
45
45
|
]
|
|
46
46
|
|
|
47
47
|
[project.optional-dependencies]
|
|
@@ -57,6 +57,7 @@ dev = [
|
|
|
57
57
|
"coverage[toml]>=7.0.0,<8.0.0",
|
|
58
58
|
"fakeredis[json,lua]>=2.34.0,<3.0.0",
|
|
59
59
|
"black>=26.1.0",
|
|
60
|
+
"ruff>=0.15.5",
|
|
60
61
|
]
|
|
61
62
|
|
|
62
63
|
[project.urls]
|
|
@@ -2,6 +2,9 @@ import pytest_asyncio
|
|
|
2
2
|
import redis.asyncio
|
|
3
3
|
from dynaconf import Dynaconf
|
|
4
4
|
|
|
5
|
+
from mageflow import start_mageflow
|
|
6
|
+
from tests.integration.hatchet.worker import TEST_MAGEFLOW_CONFIG
|
|
7
|
+
|
|
5
8
|
settings = Dynaconf(
|
|
6
9
|
envvar_prefix="DYNACONF",
|
|
7
10
|
settings_files=["settings.toml", ".secrets.toml"],
|
|
@@ -24,3 +27,8 @@ async def real_redis(redis_client):
|
|
|
24
27
|
if delete_keys:
|
|
25
28
|
await redis_client.delete(*delete_keys)
|
|
26
29
|
await redis_client.aclose()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest_asyncio.fixture(scope="function", loop_scope="session", autouse=True)
|
|
33
|
+
async def init_settings(real_redis):
|
|
34
|
+
await start_mageflow(real_redis, TEST_MAGEFLOW_CONFIG)
|
|
@@ -5,19 +5,19 @@ from hatchet_sdk import Hatchet
|
|
|
5
5
|
from hatchet_sdk.clients.rest import V1LogLineList, V1TaskStatus, V1TaskSummary
|
|
6
6
|
from hatchet_sdk.runnables.workflow import TaskRunRef
|
|
7
7
|
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from tests.integration.hatchet.conftest import extract_bad_keys_from_redis
|
|
10
|
+
from tests.integration.hatchet.worker import MAX_DONE_TTL
|
|
8
11
|
from thirdmagic.chain.model import ChainTaskSignature
|
|
9
12
|
from thirdmagic.consts import (
|
|
10
13
|
MAGEFLOW_TASK_INITIALS,
|
|
11
14
|
TASK_ID_PARAM_NAME,
|
|
12
|
-
REMOVED_TASK_TTL,
|
|
13
15
|
)
|
|
14
16
|
from thirdmagic.signature import Signature
|
|
15
17
|
from thirdmagic.swarm.model import SwarmTaskSignature
|
|
16
18
|
from thirdmagic.task import TaskSignature
|
|
17
19
|
from thirdmagic.utils import return_value_field
|
|
18
20
|
|
|
19
|
-
from tests.integration.hatchet.conftest import extract_bad_keys_from_redis
|
|
20
|
-
|
|
21
21
|
WF_MAPPING_TYPE = dict[str, V1TaskSummary]
|
|
22
22
|
WF_MAPPING_BY_WF_ID_TYPE = dict[str, V1TaskSummary]
|
|
23
23
|
HatchetRuns = list[V1TaskSummary]
|
|
@@ -209,11 +209,11 @@ async def assert_redis_is_clean(redis_client):
|
|
|
209
209
|
keys_with_invalid_ttl = [
|
|
210
210
|
(key, ttl)
|
|
211
211
|
for key, ttl in zip(non_persistent_keys, ttls)
|
|
212
|
-
if ttl == -1 or ttl >
|
|
212
|
+
if ttl == -1 or ttl > MAX_DONE_TTL
|
|
213
213
|
]
|
|
214
214
|
assert (
|
|
215
215
|
len(keys_with_invalid_ttl) == 0
|
|
216
|
-
), f"Keys without proper TTL (should be <= {
|
|
216
|
+
), f"Keys without proper TTL (should be <= {MAX_DONE_TTL}s): {keys_with_invalid_ttl}"
|
|
217
217
|
|
|
218
218
|
|
|
219
219
|
def assert_task_was_paused(runs: HatchetRuns, task: TaskSignature, with_resume=False):
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
-
from thirdmagic.task import TaskSignature
|
|
5
4
|
|
|
6
5
|
import mageflow
|
|
7
6
|
from tests.integration.hatchet.assertions import (
|
|
7
|
+
assert_chain_done,
|
|
8
|
+
assert_redis_is_clean,
|
|
8
9
|
assert_signature_not_called,
|
|
9
10
|
assert_task_was_paused,
|
|
10
|
-
assert_redis_is_clean,
|
|
11
|
-
assert_chain_done,
|
|
12
11
|
get_runs,
|
|
13
12
|
)
|
|
14
13
|
from tests.integration.hatchet.conftest import HatchetInitData
|
|
15
14
|
from tests.integration.hatchet.models import ContextMessage
|
|
16
|
-
from tests.integration.hatchet.worker import sleep_task
|
|
17
|
-
from
|
|
15
|
+
from tests.integration.hatchet.worker import sleep_task, task2_with_result
|
|
16
|
+
from thirdmagic.task import TaskSignature
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
@pytest.mark.asyncio(loop_scope="session")
|
|
@@ -11,7 +11,7 @@ from datetime import datetime
|
|
|
11
11
|
from io import BytesIO
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from threading import Thread
|
|
14
|
-
from typing import
|
|
14
|
+
from typing import AsyncGenerator, Callable, Generator
|
|
15
15
|
|
|
16
16
|
import psutil
|
|
17
17
|
import pytest
|
|
@@ -23,21 +23,20 @@ from hatchet_sdk.clients.admin import TriggerWorkflowOptions
|
|
|
23
23
|
from hatchet_sdk.clients.rest import V1TaskStatus
|
|
24
24
|
from hatchet_sdk.features.runs import BulkCancelReplayOpts, RunFilter
|
|
25
25
|
from redis.asyncio.client import Redis
|
|
26
|
-
from thirdmagic.task_def import MageflowTaskDefinition
|
|
27
26
|
|
|
28
27
|
import mageflow
|
|
29
28
|
from mageflow import Mageflow
|
|
30
29
|
from mageflow.client import HatchetMageflow
|
|
31
|
-
from mageflow.startup import init_mageflow
|
|
32
30
|
from tests.integration.hatchet.worker import (
|
|
31
|
+
chain_callback,
|
|
33
32
|
config_obj,
|
|
33
|
+
fail_task,
|
|
34
34
|
task1,
|
|
35
|
+
task1_callback,
|
|
35
36
|
task2,
|
|
36
37
|
task3,
|
|
37
|
-
task1_callback,
|
|
38
|
-
fail_task,
|
|
39
|
-
chain_callback,
|
|
40
38
|
)
|
|
39
|
+
from thirdmagic.task_def import MageflowTaskDefinition
|
|
41
40
|
|
|
42
41
|
# If redis key starts with one of these, it shouldn't be removed
|
|
43
42
|
STATIC_REDIS_PREFIX_KEYS = [MageflowTaskDefinition.__name__]
|
|
@@ -79,16 +78,6 @@ async def hatchet_client_init(
|
|
|
79
78
|
await rapyer.teardown_rapyer()
|
|
80
79
|
|
|
81
80
|
|
|
82
|
-
@pytest_asyncio.fixture(scope="function", loop_scope="session", autouse=True)
|
|
83
|
-
async def init_settings(hatchet_client_init: HatchetInitData):
|
|
84
|
-
redis_client, hatchet = (
|
|
85
|
-
hatchet_client_init.redis_client,
|
|
86
|
-
hatchet_client_init.hatchet,
|
|
87
|
-
)
|
|
88
|
-
# Load the subclasses of the task signature
|
|
89
|
-
await init_mageflow(redis_client, [])
|
|
90
|
-
|
|
91
|
-
|
|
92
81
|
@pytest_asyncio.fixture(scope="session", loop_scope="session", autouse=True)
|
|
93
82
|
async def hatchet_worker_deploy(
|
|
94
83
|
redis_client,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
4
5
|
from thirdmagic.message import ReturnValue
|
|
5
6
|
|
|
6
7
|
|
|
@@ -41,5 +42,14 @@ class SleepTaskMessage(ContextMessage):
|
|
|
41
42
|
result: Any = None
|
|
42
43
|
|
|
43
44
|
|
|
45
|
+
class SignatureKeysResult(BaseModel):
|
|
46
|
+
task_keys: list[str]
|
|
47
|
+
chain_key: str
|
|
48
|
+
chain_sub_task_keys: list[str]
|
|
49
|
+
swarm_key: str
|
|
50
|
+
swarm_sub_task_keys: list[str]
|
|
51
|
+
publish_state_key: str
|
|
52
|
+
|
|
53
|
+
|
|
44
54
|
class MageflowTestError(Exception):
|
|
45
55
|
pass
|