hatchet-sdk 1.10.0__py3-none-any.whl → 1.10.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hatchet-sdk might be problematic. Click here for more details.
- hatchet_sdk/__init__.py +5 -1
- hatchet_sdk/clients/admin.py +5 -3
- hatchet_sdk/clients/dispatcher/action_listener.py +8 -117
- hatchet_sdk/clients/dispatcher/dispatcher.py +1 -1
- hatchet_sdk/config.py +13 -0
- hatchet_sdk/opentelemetry/instrumentor.py +8 -3
- hatchet_sdk/runnables/action.py +125 -0
- hatchet_sdk/runnables/contextvars.py +6 -1
- hatchet_sdk/utils/opentelemetry.py +19 -0
- hatchet_sdk/worker/action_listener_process.py +3 -2
- hatchet_sdk/worker/runner/run_loop_manager.py +1 -1
- hatchet_sdk/worker/runner/runner.py +5 -3
- hatchet_sdk/worker/worker.py +1 -1
- {hatchet_sdk-1.10.0.dist-info → hatchet_sdk-1.10.2.dist-info}/METADATA +1 -1
- {hatchet_sdk-1.10.0.dist-info → hatchet_sdk-1.10.2.dist-info}/RECORD +17 -15
- {hatchet_sdk-1.10.0.dist-info → hatchet_sdk-1.10.2.dist-info}/WHEEL +0 -0
- {hatchet_sdk-1.10.0.dist-info → hatchet_sdk-1.10.2.dist-info}/entry_points.txt +0 -0
hatchet_sdk/__init__.py
CHANGED
|
@@ -130,7 +130,7 @@ from hatchet_sdk.clients.rest.models.workflow_version_definition import (
|
|
|
130
130
|
WorkflowVersionDefinition,
|
|
131
131
|
)
|
|
132
132
|
from hatchet_sdk.clients.rest.models.workflow_version_meta import WorkflowVersionMeta
|
|
133
|
-
from hatchet_sdk.config import ClientConfig
|
|
133
|
+
from hatchet_sdk.config import ClientConfig, ClientTLSConfig, OpenTelemetryConfig
|
|
134
134
|
from hatchet_sdk.context.context import Context, DurableContext
|
|
135
135
|
from hatchet_sdk.context.worker_context import WorkerContext
|
|
136
136
|
from hatchet_sdk.contracts.workflows_pb2 import (
|
|
@@ -149,6 +149,7 @@ from hatchet_sdk.runnables.types import (
|
|
|
149
149
|
TaskDefaults,
|
|
150
150
|
WorkflowConfig,
|
|
151
151
|
)
|
|
152
|
+
from hatchet_sdk.utils.opentelemetry import OTelAttribute
|
|
152
153
|
from hatchet_sdk.waits import (
|
|
153
154
|
Condition,
|
|
154
155
|
OrGroup,
|
|
@@ -271,4 +272,7 @@ __all__ = [
|
|
|
271
272
|
"BulkCancelReplayOpts",
|
|
272
273
|
"RunFilter",
|
|
273
274
|
"V1TaskStatus",
|
|
275
|
+
"OTelAttribute",
|
|
276
|
+
"OpenTelemetryConfig",
|
|
277
|
+
"ClientTLSConfig",
|
|
274
278
|
]
|
hatchet_sdk/clients/admin.py
CHANGED
|
@@ -20,6 +20,7 @@ from hatchet_sdk.features.runs import RunsClient
|
|
|
20
20
|
from hatchet_sdk.metadata import get_metadata
|
|
21
21
|
from hatchet_sdk.rate_limit import RateLimitDuration
|
|
22
22
|
from hatchet_sdk.runnables.contextvars import (
|
|
23
|
+
ctx_action_key,
|
|
23
24
|
ctx_step_run_id,
|
|
24
25
|
ctx_worker_id,
|
|
25
26
|
ctx_workflow_run_id,
|
|
@@ -281,11 +282,12 @@ class AdminClient:
|
|
|
281
282
|
workflow_run_id = ctx_workflow_run_id.get()
|
|
282
283
|
step_run_id = ctx_step_run_id.get()
|
|
283
284
|
worker_id = ctx_worker_id.get()
|
|
284
|
-
|
|
285
|
+
action_key = ctx_action_key.get()
|
|
286
|
+
spawn_index = workflow_spawn_indices[action_key] if action_key else 0
|
|
285
287
|
|
|
286
288
|
## Increment the spawn_index for the parent workflow
|
|
287
|
-
if
|
|
288
|
-
workflow_spawn_indices[
|
|
289
|
+
if action_key:
|
|
290
|
+
workflow_spawn_indices[action_key] += 1
|
|
289
291
|
|
|
290
292
|
desired_worker_id = (
|
|
291
293
|
(options.desired_worker_id or worker_id) if options.sticky else None
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
import time
|
|
4
|
-
from
|
|
5
|
-
from enum import Enum
|
|
6
|
-
from typing import Any, AsyncGenerator, cast
|
|
4
|
+
from typing import TYPE_CHECKING, AsyncGenerator, cast
|
|
7
5
|
|
|
8
6
|
import grpc
|
|
9
7
|
import grpc.aio
|
|
10
|
-
from pydantic import BaseModel, ConfigDict, Field,
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
11
9
|
|
|
12
10
|
from hatchet_sdk.clients.event_ts import (
|
|
13
11
|
ThreadSafeEvent,
|
|
@@ -18,7 +16,6 @@ from hatchet_sdk.clients.events import proto_timestamp_now
|
|
|
18
16
|
from hatchet_sdk.clients.listeners.run_event_listener import (
|
|
19
17
|
DEFAULT_ACTION_LISTENER_RETRY_INTERVAL,
|
|
20
18
|
)
|
|
21
|
-
from hatchet_sdk.config import ClientConfig
|
|
22
19
|
from hatchet_sdk.connection import new_conn
|
|
23
20
|
from hatchet_sdk.contracts.dispatcher_pb2 import ActionType as ActionTypeProto
|
|
24
21
|
from hatchet_sdk.contracts.dispatcher_pb2 import (
|
|
@@ -31,10 +28,15 @@ from hatchet_sdk.contracts.dispatcher_pb2 import (
|
|
|
31
28
|
from hatchet_sdk.contracts.dispatcher_pb2_grpc import DispatcherStub
|
|
32
29
|
from hatchet_sdk.logger import logger
|
|
33
30
|
from hatchet_sdk.metadata import get_metadata
|
|
31
|
+
from hatchet_sdk.runnables.action import Action, ActionPayload, ActionType
|
|
34
32
|
from hatchet_sdk.utils.backoff import exp_backoff_sleep
|
|
35
33
|
from hatchet_sdk.utils.proto_enums import convert_proto_enum_to_python
|
|
36
34
|
from hatchet_sdk.utils.typing import JSONSerializableMapping
|
|
37
35
|
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from hatchet_sdk.config import ClientConfig
|
|
38
|
+
|
|
39
|
+
|
|
38
40
|
DEFAULT_ACTION_TIMEOUT = 600 # seconds
|
|
39
41
|
DEFAULT_ACTION_LISTENER_RETRY_COUNT = 15
|
|
40
42
|
|
|
@@ -63,117 +65,6 @@ class GetActionListenerRequest(BaseModel):
|
|
|
63
65
|
return self
|
|
64
66
|
|
|
65
67
|
|
|
66
|
-
class ActionPayload(BaseModel):
|
|
67
|
-
model_config = ConfigDict(extra="allow")
|
|
68
|
-
|
|
69
|
-
input: JSONSerializableMapping = Field(default_factory=dict)
|
|
70
|
-
parents: dict[str, JSONSerializableMapping] = Field(default_factory=dict)
|
|
71
|
-
overrides: JSONSerializableMapping = Field(default_factory=dict)
|
|
72
|
-
user_data: JSONSerializableMapping = Field(default_factory=dict)
|
|
73
|
-
step_run_errors: dict[str, str] = Field(default_factory=dict)
|
|
74
|
-
triggered_by: str | None = None
|
|
75
|
-
triggers: JSONSerializableMapping = Field(default_factory=dict)
|
|
76
|
-
filter_payload: JSONSerializableMapping = Field(default_factory=dict)
|
|
77
|
-
|
|
78
|
-
@field_validator(
|
|
79
|
-
"input",
|
|
80
|
-
"parents",
|
|
81
|
-
"overrides",
|
|
82
|
-
"user_data",
|
|
83
|
-
"step_run_errors",
|
|
84
|
-
"filter_payload",
|
|
85
|
-
mode="before",
|
|
86
|
-
)
|
|
87
|
-
@classmethod
|
|
88
|
-
def validate_fields(cls, v: Any) -> Any:
|
|
89
|
-
return v or {}
|
|
90
|
-
|
|
91
|
-
@model_validator(mode="after")
|
|
92
|
-
def validate_filter_payload(self) -> "ActionPayload":
|
|
93
|
-
self.filter_payload = self.triggers.get("filter_payload", {})
|
|
94
|
-
|
|
95
|
-
return self
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
class ActionType(str, Enum):
|
|
99
|
-
START_STEP_RUN = "START_STEP_RUN"
|
|
100
|
-
CANCEL_STEP_RUN = "CANCEL_STEP_RUN"
|
|
101
|
-
START_GET_GROUP_KEY = "START_GET_GROUP_KEY"
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
ActionKey = str
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
class Action(BaseModel):
|
|
108
|
-
worker_id: str
|
|
109
|
-
tenant_id: str
|
|
110
|
-
workflow_run_id: str
|
|
111
|
-
workflow_id: str | None = None
|
|
112
|
-
workflow_version_id: str | None = None
|
|
113
|
-
get_group_key_run_id: str
|
|
114
|
-
job_id: str
|
|
115
|
-
job_name: str
|
|
116
|
-
job_run_id: str
|
|
117
|
-
step_id: str
|
|
118
|
-
step_run_id: str
|
|
119
|
-
action_id: str
|
|
120
|
-
action_type: ActionType
|
|
121
|
-
retry_count: int
|
|
122
|
-
action_payload: ActionPayload
|
|
123
|
-
additional_metadata: JSONSerializableMapping = field(default_factory=dict)
|
|
124
|
-
|
|
125
|
-
child_workflow_index: int | None = None
|
|
126
|
-
child_workflow_key: str | None = None
|
|
127
|
-
parent_workflow_run_id: str | None = None
|
|
128
|
-
|
|
129
|
-
priority: int | None = None
|
|
130
|
-
|
|
131
|
-
def _dump_payload_to_str(self) -> str:
|
|
132
|
-
try:
|
|
133
|
-
return json.dumps(self.action_payload.model_dump(), default=str)
|
|
134
|
-
except Exception:
|
|
135
|
-
return str(self.action_payload)
|
|
136
|
-
|
|
137
|
-
@property
|
|
138
|
-
def otel_attributes(self) -> dict[str, str | int]:
|
|
139
|
-
try:
|
|
140
|
-
payload_str = json.dumps(self.action_payload.model_dump(), default=str)
|
|
141
|
-
except Exception:
|
|
142
|
-
payload_str = str(self.action_payload)
|
|
143
|
-
|
|
144
|
-
attrs: dict[str, str | int | None] = {
|
|
145
|
-
"hatchet.tenant_id": self.tenant_id,
|
|
146
|
-
"hatchet.worker_id": self.worker_id,
|
|
147
|
-
"hatchet.workflow_run_id": self.workflow_run_id,
|
|
148
|
-
"hatchet.step_id": self.step_id,
|
|
149
|
-
"hatchet.step_run_id": self.step_run_id,
|
|
150
|
-
"hatchet.retry_count": self.retry_count,
|
|
151
|
-
"hatchet.parent_workflow_run_id": self.parent_workflow_run_id,
|
|
152
|
-
"hatchet.child_workflow_index": self.child_workflow_index,
|
|
153
|
-
"hatchet.child_workflow_key": self.child_workflow_key,
|
|
154
|
-
"hatchet.action_payload": payload_str,
|
|
155
|
-
"hatchet.workflow_name": self.job_name,
|
|
156
|
-
"hatchet.action_name": self.action_id,
|
|
157
|
-
"hatchet.get_group_key_run_id": self.get_group_key_run_id,
|
|
158
|
-
"hatchet.workflow_id": self.workflow_id,
|
|
159
|
-
"hatchet.workflow_version_id": self.workflow_version_id,
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return {k: v for k, v in attrs.items() if v}
|
|
163
|
-
|
|
164
|
-
@property
|
|
165
|
-
def key(self) -> ActionKey:
|
|
166
|
-
"""
|
|
167
|
-
This key is used to uniquely identify a single step run by its id + retry count.
|
|
168
|
-
It's used when storing references to a task, a context, etc. in a dictionary so that
|
|
169
|
-
we can look up those items in the dictionary by a unique key.
|
|
170
|
-
"""
|
|
171
|
-
if self.action_type == ActionType.START_GET_GROUP_KEY:
|
|
172
|
-
return f"{self.get_group_key_run_id}/{self.retry_count}"
|
|
173
|
-
else:
|
|
174
|
-
return f"{self.step_run_id}/{self.retry_count}"
|
|
175
|
-
|
|
176
|
-
|
|
177
68
|
def parse_additional_metadata(additional_metadata: str) -> JSONSerializableMapping:
|
|
178
69
|
try:
|
|
179
70
|
return cast(
|
|
@@ -185,7 +76,7 @@ def parse_additional_metadata(additional_metadata: str) -> JSONSerializableMappi
|
|
|
185
76
|
|
|
186
77
|
|
|
187
78
|
class ActionListener:
|
|
188
|
-
def __init__(self, config: ClientConfig, worker_id: str) -> None:
|
|
79
|
+
def __init__(self, config: "ClientConfig", worker_id: str) -> None:
|
|
189
80
|
self.config = config
|
|
190
81
|
self.worker_id = worker_id
|
|
191
82
|
|
|
@@ -4,7 +4,6 @@ import grpc.aio
|
|
|
4
4
|
from google.protobuf.timestamp_pb2 import Timestamp
|
|
5
5
|
|
|
6
6
|
from hatchet_sdk.clients.dispatcher.action_listener import (
|
|
7
|
-
Action,
|
|
8
7
|
ActionListener,
|
|
9
8
|
GetActionListenerRequest,
|
|
10
9
|
)
|
|
@@ -29,6 +28,7 @@ from hatchet_sdk.contracts.dispatcher_pb2 import (
|
|
|
29
28
|
)
|
|
30
29
|
from hatchet_sdk.contracts.dispatcher_pb2_grpc import DispatcherStub
|
|
31
30
|
from hatchet_sdk.metadata import get_metadata
|
|
31
|
+
from hatchet_sdk.runnables.action import Action
|
|
32
32
|
|
|
33
33
|
DEFAULT_REGISTER_TIMEOUT = 30
|
|
34
34
|
|
hatchet_sdk/config.py
CHANGED
|
@@ -6,6 +6,7 @@ from pydantic import Field, field_validator, model_validator
|
|
|
6
6
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
7
7
|
|
|
8
8
|
from hatchet_sdk.token import get_addresses_from_jwt, get_tenant_id_from_jwt
|
|
9
|
+
from hatchet_sdk.utils.opentelemetry import OTelAttribute
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def create_settings_config(env_prefix: str) -> SettingsConfigDict:
|
|
@@ -37,6 +38,17 @@ class HealthcheckConfig(BaseSettings):
|
|
|
37
38
|
enabled: bool = False
|
|
38
39
|
|
|
39
40
|
|
|
41
|
+
class OpenTelemetryConfig(BaseSettings):
|
|
42
|
+
model_config = create_settings_config(
|
|
43
|
+
env_prefix="HATCHET_CLIENT_OPENTELEMETRY_",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
excluded_attributes: list[OTelAttribute] = Field(
|
|
47
|
+
default_factory=list,
|
|
48
|
+
description='Note that if specifying this field via an environment variable, the variable must be a valid JSON array. For example: \'["action_name", "action_payload"]\'',
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
40
52
|
DEFAULT_HOST_PORT = "localhost:7070"
|
|
41
53
|
|
|
42
54
|
|
|
@@ -55,6 +67,7 @@ class ClientConfig(BaseSettings):
|
|
|
55
67
|
|
|
56
68
|
tls_config: ClientTLSConfig = Field(default_factory=lambda: ClientTLSConfig())
|
|
57
69
|
healthcheck: HealthcheckConfig = Field(default_factory=lambda: HealthcheckConfig())
|
|
70
|
+
otel: OpenTelemetryConfig = Field(default_factory=lambda: OpenTelemetryConfig())
|
|
58
71
|
|
|
59
72
|
listener_v2_timeout: int | None = None
|
|
60
73
|
grpc_max_recv_message_length: int = Field(
|
|
@@ -27,18 +27,19 @@ except (RuntimeError, ImportError, ModuleNotFoundError):
|
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
import hatchet_sdk
|
|
30
|
+
from hatchet_sdk import ClientConfig
|
|
30
31
|
from hatchet_sdk.clients.admin import (
|
|
31
32
|
AdminClient,
|
|
32
33
|
TriggerWorkflowOptions,
|
|
33
34
|
WorkflowRunTriggerConfig,
|
|
34
35
|
)
|
|
35
|
-
from hatchet_sdk.clients.dispatcher.action_listener import Action
|
|
36
36
|
from hatchet_sdk.clients.events import (
|
|
37
37
|
BulkPushEventWithMetadata,
|
|
38
38
|
EventClient,
|
|
39
39
|
PushEventOptions,
|
|
40
40
|
)
|
|
41
41
|
from hatchet_sdk.contracts.events_pb2 import Event
|
|
42
|
+
from hatchet_sdk.runnables.action import Action
|
|
42
43
|
from hatchet_sdk.worker.runner.runner import Runner
|
|
43
44
|
from hatchet_sdk.workflow_run import WorkflowRunRef
|
|
44
45
|
|
|
@@ -148,13 +149,17 @@ class HatchetInstrumentor(BaseInstrumentor): # type: ignore[misc]
|
|
|
148
149
|
If not provided, the global tracer provider will be used.
|
|
149
150
|
:param meter_provider: MeterProvider | None: The OpenTelemetry MeterProvider to use.
|
|
150
151
|
If not provided, a no-op meter provider will be used.
|
|
152
|
+
:param config: ClientConfig | None: The configuration for the Hatchet client. If not provided,
|
|
153
|
+
a default configuration will be used.
|
|
151
154
|
"""
|
|
152
155
|
|
|
153
156
|
def __init__(
|
|
154
157
|
self,
|
|
155
158
|
tracer_provider: TracerProvider | None = None,
|
|
156
159
|
meter_provider: MeterProvider | None = None,
|
|
160
|
+
config: ClientConfig | None = None,
|
|
157
161
|
):
|
|
162
|
+
self.config = config or ClientConfig()
|
|
158
163
|
|
|
159
164
|
self.tracer_provider = tracer_provider or get_tracer_provider()
|
|
160
165
|
self.meter_provider = meter_provider or NoOpMeterProvider()
|
|
@@ -233,7 +238,7 @@ class HatchetInstrumentor(BaseInstrumentor): # type: ignore[misc]
|
|
|
233
238
|
|
|
234
239
|
with self._tracer.start_as_current_span(
|
|
235
240
|
"hatchet.start_step_run",
|
|
236
|
-
attributes=action.
|
|
241
|
+
attributes=action.get_otel_attributes(self.config),
|
|
237
242
|
context=traceparent,
|
|
238
243
|
) as span:
|
|
239
244
|
result = await wrapped(*args, **kwargs)
|
|
@@ -255,7 +260,7 @@ class HatchetInstrumentor(BaseInstrumentor): # type: ignore[misc]
|
|
|
255
260
|
|
|
256
261
|
with self._tracer.start_as_current_span(
|
|
257
262
|
"hatchet.get_group_key_run",
|
|
258
|
-
attributes=action.
|
|
263
|
+
attributes=action.get_otel_attributes(self.config),
|
|
259
264
|
) as span:
|
|
260
265
|
result = await wrapped(*args, **kwargs)
|
|
261
266
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from dataclasses import field
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
|
7
|
+
|
|
8
|
+
from hatchet_sdk.utils.opentelemetry import OTelAttribute
|
|
9
|
+
from hatchet_sdk.utils.typing import JSONSerializableMapping
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from hatchet_sdk.config import ClientConfig
|
|
13
|
+
|
|
14
|
+
ActionKey = str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ActionPayload(BaseModel):
|
|
18
|
+
model_config = ConfigDict(extra="allow")
|
|
19
|
+
|
|
20
|
+
input: JSONSerializableMapping = Field(default_factory=dict)
|
|
21
|
+
parents: dict[str, JSONSerializableMapping] = Field(default_factory=dict)
|
|
22
|
+
overrides: JSONSerializableMapping = Field(default_factory=dict)
|
|
23
|
+
user_data: JSONSerializableMapping = Field(default_factory=dict)
|
|
24
|
+
step_run_errors: dict[str, str] = Field(default_factory=dict)
|
|
25
|
+
triggered_by: str | None = None
|
|
26
|
+
triggers: JSONSerializableMapping = Field(default_factory=dict)
|
|
27
|
+
filter_payload: JSONSerializableMapping = Field(default_factory=dict)
|
|
28
|
+
|
|
29
|
+
@field_validator(
|
|
30
|
+
"input",
|
|
31
|
+
"parents",
|
|
32
|
+
"overrides",
|
|
33
|
+
"user_data",
|
|
34
|
+
"step_run_errors",
|
|
35
|
+
"filter_payload",
|
|
36
|
+
mode="before",
|
|
37
|
+
)
|
|
38
|
+
@classmethod
|
|
39
|
+
def validate_fields(cls, v: Any) -> Any:
|
|
40
|
+
return v or {}
|
|
41
|
+
|
|
42
|
+
@model_validator(mode="after")
|
|
43
|
+
def validate_filter_payload(self) -> "ActionPayload":
|
|
44
|
+
self.filter_payload = self.triggers.get("filter_payload", {})
|
|
45
|
+
|
|
46
|
+
return self
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ActionType(str, Enum):
|
|
50
|
+
START_STEP_RUN = "START_STEP_RUN"
|
|
51
|
+
CANCEL_STEP_RUN = "CANCEL_STEP_RUN"
|
|
52
|
+
START_GET_GROUP_KEY = "START_GET_GROUP_KEY"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Action(BaseModel):
|
|
56
|
+
worker_id: str
|
|
57
|
+
tenant_id: str
|
|
58
|
+
workflow_run_id: str
|
|
59
|
+
workflow_id: str | None = None
|
|
60
|
+
workflow_version_id: str | None = None
|
|
61
|
+
get_group_key_run_id: str
|
|
62
|
+
job_id: str
|
|
63
|
+
job_name: str
|
|
64
|
+
job_run_id: str
|
|
65
|
+
step_id: str
|
|
66
|
+
step_run_id: str
|
|
67
|
+
action_id: str
|
|
68
|
+
action_type: ActionType
|
|
69
|
+
retry_count: int
|
|
70
|
+
action_payload: ActionPayload
|
|
71
|
+
additional_metadata: JSONSerializableMapping = field(default_factory=dict)
|
|
72
|
+
|
|
73
|
+
child_workflow_index: int | None = None
|
|
74
|
+
child_workflow_key: str | None = None
|
|
75
|
+
parent_workflow_run_id: str | None = None
|
|
76
|
+
|
|
77
|
+
priority: int | None = None
|
|
78
|
+
|
|
79
|
+
def _dump_payload_to_str(self) -> str:
|
|
80
|
+
try:
|
|
81
|
+
return json.dumps(self.action_payload.model_dump(), default=str)
|
|
82
|
+
except Exception:
|
|
83
|
+
return str(self.action_payload)
|
|
84
|
+
|
|
85
|
+
def get_otel_attributes(self, config: "ClientConfig") -> dict[str, str | int]:
|
|
86
|
+
try:
|
|
87
|
+
payload_str = json.dumps(self.action_payload.model_dump(), default=str)
|
|
88
|
+
except Exception:
|
|
89
|
+
payload_str = str(self.action_payload)
|
|
90
|
+
|
|
91
|
+
attrs: dict[OTelAttribute, str | int | None] = {
|
|
92
|
+
OTelAttribute.TENANT_ID: self.tenant_id,
|
|
93
|
+
OTelAttribute.WORKER_ID: self.worker_id,
|
|
94
|
+
OTelAttribute.WORKFLOW_RUN_ID: self.workflow_run_id,
|
|
95
|
+
OTelAttribute.STEP_ID: self.step_id,
|
|
96
|
+
OTelAttribute.STEP_RUN_ID: self.step_run_id,
|
|
97
|
+
OTelAttribute.RETRY_COUNT: self.retry_count,
|
|
98
|
+
OTelAttribute.PARENT_WORKFLOW_RUN_ID: self.parent_workflow_run_id,
|
|
99
|
+
OTelAttribute.CHILD_WORKFLOW_INDEX: self.child_workflow_index,
|
|
100
|
+
OTelAttribute.CHILD_WORKFLOW_KEY: self.child_workflow_key,
|
|
101
|
+
OTelAttribute.ACTION_PAYLOAD: payload_str,
|
|
102
|
+
OTelAttribute.WORKFLOW_NAME: self.job_name,
|
|
103
|
+
OTelAttribute.ACTION_NAME: self.action_id,
|
|
104
|
+
OTelAttribute.GET_GROUP_KEY_RUN_ID: self.get_group_key_run_id,
|
|
105
|
+
OTelAttribute.WORKFLOW_ID: self.workflow_id,
|
|
106
|
+
OTelAttribute.WORKFLOW_VERSION_ID: self.workflow_version_id,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
f"hatchet.{k.value}": v
|
|
111
|
+
for k, v in attrs.items()
|
|
112
|
+
if v and k not in config.otel.excluded_attributes
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def key(self) -> ActionKey:
|
|
117
|
+
"""
|
|
118
|
+
This key is used to uniquely identify a single step run by its id + retry count.
|
|
119
|
+
It's used when storing references to a task, a context, etc. in a dictionary so that
|
|
120
|
+
we can look up those items in the dictionary by a unique key.
|
|
121
|
+
"""
|
|
122
|
+
if self.action_type == ActionType.START_GET_GROUP_KEY:
|
|
123
|
+
return f"{self.get_group_key_run_id}/{self.retry_count}"
|
|
124
|
+
else:
|
|
125
|
+
return f"{self.step_run_id}/{self.retry_count}"
|
|
@@ -2,11 +2,16 @@ import asyncio
|
|
|
2
2
|
from collections import Counter
|
|
3
3
|
from contextvars import ContextVar
|
|
4
4
|
|
|
5
|
+
from hatchet_sdk.runnables.action import ActionKey
|
|
6
|
+
|
|
5
7
|
ctx_workflow_run_id: ContextVar[str | None] = ContextVar(
|
|
6
8
|
"ctx_workflow_run_id", default=None
|
|
7
9
|
)
|
|
10
|
+
ctx_action_key: ContextVar[ActionKey | None] = ContextVar(
|
|
11
|
+
"ctx_action_key", default=None
|
|
12
|
+
)
|
|
8
13
|
ctx_step_run_id: ContextVar[str | None] = ContextVar("ctx_step_run_id", default=None)
|
|
9
14
|
ctx_worker_id: ContextVar[str | None] = ContextVar("ctx_worker_id", default=None)
|
|
10
15
|
|
|
11
|
-
workflow_spawn_indices = Counter[
|
|
16
|
+
workflow_spawn_indices = Counter[ActionKey]()
|
|
12
17
|
spawn_index_lock = asyncio.Lock()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class OTelAttribute(str, Enum):
|
|
5
|
+
ACTION_NAME = "action_name"
|
|
6
|
+
ACTION_PAYLOAD = "action_payload"
|
|
7
|
+
CHILD_WORKFLOW_INDEX = "child_workflow_index"
|
|
8
|
+
CHILD_WORKFLOW_KEY = "child_workflow_key"
|
|
9
|
+
GET_GROUP_KEY_RUN_ID = "get_group_key_run_id"
|
|
10
|
+
PARENT_WORKFLOW_RUN_ID = "parent_workflow_run_id"
|
|
11
|
+
RETRY_COUNT = "retry_count"
|
|
12
|
+
STEP_ID = "step_id"
|
|
13
|
+
STEP_RUN_ID = "step_run_id"
|
|
14
|
+
TENANT_ID = "tenant_id"
|
|
15
|
+
WORKER_ID = "worker_id"
|
|
16
|
+
WORKFLOW_ID = "workflow_id"
|
|
17
|
+
WORKFLOW_NAME = "workflow_name"
|
|
18
|
+
WORKFLOW_RUN_ID = "workflow_run_id"
|
|
19
|
+
WORKFLOW_VERSION_ID = "workflow_version_id"
|
|
@@ -10,9 +10,7 @@ import grpc
|
|
|
10
10
|
|
|
11
11
|
from hatchet_sdk.client import Client
|
|
12
12
|
from hatchet_sdk.clients.dispatcher.action_listener import (
|
|
13
|
-
Action,
|
|
14
13
|
ActionListener,
|
|
15
|
-
ActionType,
|
|
16
14
|
GetActionListenerRequest,
|
|
17
15
|
)
|
|
18
16
|
from hatchet_sdk.clients.dispatcher.dispatcher import DispatcherClient
|
|
@@ -23,7 +21,9 @@ from hatchet_sdk.contracts.dispatcher_pb2 import (
|
|
|
23
21
|
STEP_EVENT_TYPE_STARTED,
|
|
24
22
|
)
|
|
25
23
|
from hatchet_sdk.logger import logger
|
|
24
|
+
from hatchet_sdk.runnables.action import Action, ActionType
|
|
26
25
|
from hatchet_sdk.runnables.contextvars import (
|
|
26
|
+
ctx_action_key,
|
|
27
27
|
ctx_step_run_id,
|
|
28
28
|
ctx_worker_id,
|
|
29
29
|
ctx_workflow_run_id,
|
|
@@ -230,6 +230,7 @@ class WorkerActionListenerProcess:
|
|
|
230
230
|
ctx_step_run_id.set(action.step_run_id)
|
|
231
231
|
ctx_workflow_run_id.set(action.workflow_run_id)
|
|
232
232
|
ctx_worker_id.set(action.worker_id)
|
|
233
|
+
ctx_action_key.set(action.key)
|
|
233
234
|
|
|
234
235
|
# Process the action here
|
|
235
236
|
match action.action_type:
|
|
@@ -4,9 +4,9 @@ from multiprocessing import Queue
|
|
|
4
4
|
from typing import Any, Literal, TypeVar
|
|
5
5
|
|
|
6
6
|
from hatchet_sdk.client import Client
|
|
7
|
-
from hatchet_sdk.clients.dispatcher.action_listener import Action
|
|
8
7
|
from hatchet_sdk.config import ClientConfig
|
|
9
8
|
from hatchet_sdk.logger import logger
|
|
9
|
+
from hatchet_sdk.runnables.action import Action
|
|
10
10
|
from hatchet_sdk.runnables.task import Task
|
|
11
11
|
from hatchet_sdk.worker.action_listener_process import ActionEvent
|
|
12
12
|
from hatchet_sdk.worker.runner.runner import Runner
|
|
@@ -14,7 +14,6 @@ from pydantic import BaseModel
|
|
|
14
14
|
|
|
15
15
|
from hatchet_sdk.client import Client
|
|
16
16
|
from hatchet_sdk.clients.admin import AdminClient
|
|
17
|
-
from hatchet_sdk.clients.dispatcher.action_listener import Action, ActionKey, ActionType
|
|
18
17
|
from hatchet_sdk.clients.dispatcher.dispatcher import DispatcherClient
|
|
19
18
|
from hatchet_sdk.clients.events import EventClient
|
|
20
19
|
from hatchet_sdk.clients.listeners.durable_event_listener import DurableEventListener
|
|
@@ -34,7 +33,9 @@ from hatchet_sdk.contracts.dispatcher_pb2 import (
|
|
|
34
33
|
from hatchet_sdk.exceptions import NonRetryableException
|
|
35
34
|
from hatchet_sdk.features.runs import RunsClient
|
|
36
35
|
from hatchet_sdk.logger import logger
|
|
36
|
+
from hatchet_sdk.runnables.action import Action, ActionKey, ActionType
|
|
37
37
|
from hatchet_sdk.runnables.contextvars import (
|
|
38
|
+
ctx_action_key,
|
|
38
39
|
ctx_step_run_id,
|
|
39
40
|
ctx_worker_id,
|
|
40
41
|
ctx_workflow_run_id,
|
|
@@ -244,6 +245,7 @@ class Runner:
|
|
|
244
245
|
ctx_step_run_id.set(action.step_run_id)
|
|
245
246
|
ctx_workflow_run_id.set(action.workflow_run_id)
|
|
246
247
|
ctx_worker_id.set(action.worker_id)
|
|
248
|
+
ctx_action_key.set(action.key)
|
|
247
249
|
|
|
248
250
|
try:
|
|
249
251
|
if task.is_async_function:
|
|
@@ -388,9 +390,9 @@ class Runner:
|
|
|
388
390
|
|
|
389
391
|
## Once the step run completes, we need to remove the workflow spawn index
|
|
390
392
|
## so we don't leak memory
|
|
391
|
-
if action.
|
|
393
|
+
if action.key in workflow_spawn_indices:
|
|
392
394
|
async with spawn_index_lock:
|
|
393
|
-
workflow_spawn_indices.pop(action.
|
|
395
|
+
workflow_spawn_indices.pop(action.key)
|
|
394
396
|
|
|
395
397
|
## IMPORTANT: Keep this method's signature in sync with the wrapper in the OTel instrumentor
|
|
396
398
|
async def handle_start_group_key_run(self, action: Action) -> Exception | None:
|
hatchet_sdk/worker/worker.py
CHANGED
|
@@ -21,10 +21,10 @@ from prometheus_client import Gauge, generate_latest
|
|
|
21
21
|
from pydantic import BaseModel
|
|
22
22
|
|
|
23
23
|
from hatchet_sdk.client import Client
|
|
24
|
-
from hatchet_sdk.clients.dispatcher.action_listener import Action
|
|
25
24
|
from hatchet_sdk.config import ClientConfig
|
|
26
25
|
from hatchet_sdk.contracts.v1.workflows_pb2 import CreateWorkflowVersionRequest
|
|
27
26
|
from hatchet_sdk.logger import logger
|
|
27
|
+
from hatchet_sdk.runnables.action import Action
|
|
28
28
|
from hatchet_sdk.runnables.task import Task
|
|
29
29
|
from hatchet_sdk.runnables.workflow import BaseWorkflow
|
|
30
30
|
from hatchet_sdk.worker.action_listener_process import (
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
hatchet_sdk/__init__.py,sha256=
|
|
1
|
+
hatchet_sdk/__init__.py,sha256=TW5T_It9W79Lfu-QaxMYBXMjYaXQdOpOS6bvYJm6VjQ,10198
|
|
2
2
|
hatchet_sdk/client.py,sha256=7ONBiE29OKQci3Qaz7U5e-WSxO_ohnWq8F-MSgzG2fU,2312
|
|
3
|
-
hatchet_sdk/clients/admin.py,sha256=
|
|
4
|
-
hatchet_sdk/clients/dispatcher/action_listener.py,sha256=
|
|
5
|
-
hatchet_sdk/clients/dispatcher/dispatcher.py,sha256=
|
|
3
|
+
hatchet_sdk/clients/admin.py,sha256=7Nxtn1KemJTjWs0yEaU7OyXIFQNFUF3NzLd2E_kJmMQ,16929
|
|
4
|
+
hatchet_sdk/clients/dispatcher/action_listener.py,sha256=XUDXye-HW4V30DBgjd_dmq7e03AS9eBxUJi1VsvfSqE,13837
|
|
5
|
+
hatchet_sdk/clients/dispatcher/dispatcher.py,sha256=LoxQEX5FS6v-qKRtOXoqsx3VlBUgFgTkbK58LU1eU3I,8176
|
|
6
6
|
hatchet_sdk/clients/event_ts.py,sha256=MudFszIb9IcPKQYvBTzcatPkcWEy3nxbAtEQ0_NYxMg,2094
|
|
7
7
|
hatchet_sdk/clients/events.py,sha256=6nfZogeEgXC0Tzq6RDCTVPcBvLBVBlEonlTo-ticfrc,7200
|
|
8
8
|
hatchet_sdk/clients/listeners/durable_event_listener.py,sha256=jpqnbZsuouWk3XaOIYL9apaGtVk65eKKq66eBP9klBs,4085
|
|
@@ -227,7 +227,7 @@ hatchet_sdk/clients/rest/models/workflow_workers_count.py,sha256=qhzqfvjjIDyARki
|
|
|
227
227
|
hatchet_sdk/clients/rest/rest.py,sha256=zZHTzgl-NBdcK6XhG23m_s9RKRONGPPItzGe407s7GA,9262
|
|
228
228
|
hatchet_sdk/clients/rest/tenacity_utils.py,sha256=n6QvwuGwinLQpiWNU5GxrDNhFBE8_wZdg3WNur21rJ0,1055
|
|
229
229
|
hatchet_sdk/clients/v1/api_client.py,sha256=mJQUZ3cOxlFJiwWKK5F8jBxcpNZ7A2292HucrBqurbg,1205
|
|
230
|
-
hatchet_sdk/config.py,sha256=
|
|
230
|
+
hatchet_sdk/config.py,sha256=iQFnT8teWiov0c2l1BzByBuzn2fgKOxvMt5Yt33oLsE,5128
|
|
231
231
|
hatchet_sdk/connection.py,sha256=B5gT5NL9BBB5-l9U_cN6pMlraQk880rEYMnqaK_dgL0,2590
|
|
232
232
|
hatchet_sdk/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
233
233
|
hatchet_sdk/context/context.py,sha256=ue6ewQZ_y2k2DQ5fVvCrArV7sI-Q7fpehCkadZvi1vg,9334
|
|
@@ -264,16 +264,18 @@ hatchet_sdk/hatchet.py,sha256=BVWSrKL6GCzarTNAte_r0iWsaBXntxNzrR-syR6qENI,22885
|
|
|
264
264
|
hatchet_sdk/labels.py,sha256=nATgxWE3lFxRTnfISEpoIRLGbMfAZsHF4lZTuG4Mfic,182
|
|
265
265
|
hatchet_sdk/logger.py,sha256=5uOr52T4mImSQm1QvWT8HvZFK5WfPNh3Y1cBQZRFgUQ,333
|
|
266
266
|
hatchet_sdk/metadata.py,sha256=XkRbhnghJJGCdVvF-uzyGBcNaTqpeQ3uiQvNNP1wyBc,107
|
|
267
|
-
hatchet_sdk/opentelemetry/instrumentor.py,sha256=
|
|
267
|
+
hatchet_sdk/opentelemetry/instrumentor.py,sha256=TMv2cks2O6026dZ0FGLcMGswCcKNjAFv4dSkRCnqxIw,14108
|
|
268
268
|
hatchet_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
269
269
|
hatchet_sdk/rate_limit.py,sha256=TwbCuggiZaWpYuo4mjVLlE-z1OfQ2mRBiVvCSaG3lv4,3919
|
|
270
|
-
hatchet_sdk/runnables/
|
|
270
|
+
hatchet_sdk/runnables/action.py,sha256=-CJ1b0SnLdXvWH9HZhhoAmKO68WJRGRn7Y7vcLNr1NU,4396
|
|
271
|
+
hatchet_sdk/runnables/contextvars.py,sha256=3hXlW03FNg2sEq8_A7wMPf_c7FTNuZqg5U9n2S2_hgg,580
|
|
271
272
|
hatchet_sdk/runnables/standalone.py,sha256=5MKyLIScJbTHc6N3lRJc6L2Y_9SZj_dQWLV9oA46kqk,15663
|
|
272
273
|
hatchet_sdk/runnables/task.py,sha256=AOpULMr3hqxn4W58Lh9oEvsXn_7PPB_c_sIqHRfQn5Q,7063
|
|
273
274
|
hatchet_sdk/runnables/types.py,sha256=5jf1c7_0QVUFh0bcXi4hIiaOdUiyhspU4LltoAFCwlM,3776
|
|
274
275
|
hatchet_sdk/runnables/workflow.py,sha256=lgN2z9or8E4jgHf6C31Kf1KsTa8_rEtE0AwIqGpNEH8,39926
|
|
275
276
|
hatchet_sdk/token.py,sha256=KjIiInwG5Kqd_FO4BSW1x_5Uc7PFbnzIVJqr50-ZldE,779
|
|
276
277
|
hatchet_sdk/utils/backoff.py,sha256=6B5Rb5nLKw_TqqgpJMYjIBV1PTTtbOMRZCveisVhg_I,353
|
|
278
|
+
hatchet_sdk/utils/opentelemetry.py,sha256=EadD6jy6J_kTBqDukH5ufg7kJA_VF8LLKUd-NpSZoD8,626
|
|
277
279
|
hatchet_sdk/utils/proto_enums.py,sha256=0UybwE3s7TcqmzoQSO8YnhgAKOS8WZXsyPchB8-eksw,1247
|
|
278
280
|
hatchet_sdk/utils/timedelta_to_expression.py,sha256=kwuYZ51JdDdc3h9Sw4vgBFmJBMPkgbGJA4v9uO4_NGk,660
|
|
279
281
|
hatchet_sdk/utils/typing.py,sha256=P6-Nd5K_Hk-VhEkGj6LYki_9ddw05rJtzRA56qGXHag,914
|
|
@@ -510,13 +512,13 @@ hatchet_sdk/v0/workflow.py,sha256=d4o425efk7J3JgLIge34MW_A3pzwnwSRtwEOgIqM2pc,93
|
|
|
510
512
|
hatchet_sdk/v0/workflow_run.py,sha256=jsEZprXshrSV7i_TtL5uoCL03D18zQ3NeJCq7mp97Dg,1752
|
|
511
513
|
hatchet_sdk/waits.py,sha256=L2xZUcmrQX-pTVXWv1W8suMoYU_eA0uowpollauQmOM,3893
|
|
512
514
|
hatchet_sdk/worker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
513
|
-
hatchet_sdk/worker/action_listener_process.py,sha256=
|
|
514
|
-
hatchet_sdk/worker/runner/run_loop_manager.py,sha256=
|
|
515
|
-
hatchet_sdk/worker/runner/runner.py,sha256=
|
|
515
|
+
hatchet_sdk/worker/action_listener_process.py,sha256=6WLtKLn8dRikzHLWwkHkoMuEf18WTKbxwNoT5WlPOBk,11874
|
|
516
|
+
hatchet_sdk/worker/runner/run_loop_manager.py,sha256=E3KD1r0-BBKUWlZJMDo9e2HHl4uAyvL8gX42-JCRLLc,3794
|
|
517
|
+
hatchet_sdk/worker/runner/runner.py,sha256=9Z3Ac6hRh5rjet7-BRtpcFARlhWvy6TNrdsdj61k_yk,18822
|
|
516
518
|
hatchet_sdk/worker/runner/utils/capture_logs.py,sha256=nHRPSiDBqzhObM7i2X7t03OupVFnE7kQBdR2Ckgg-2w,2709
|
|
517
|
-
hatchet_sdk/worker/worker.py,sha256=
|
|
519
|
+
hatchet_sdk/worker/worker.py,sha256=SAucu3dg8lkKLbzb3noNWvClZ09lC5tuRcBkYZtIJ-0,16088
|
|
518
520
|
hatchet_sdk/workflow_run.py,sha256=ZwH0HLFGFVXz6jbiqSv4w0Om2XuR52Tzzw6LH4y65jQ,2765
|
|
519
|
-
hatchet_sdk-1.10.
|
|
520
|
-
hatchet_sdk-1.10.
|
|
521
|
-
hatchet_sdk-1.10.
|
|
522
|
-
hatchet_sdk-1.10.
|
|
521
|
+
hatchet_sdk-1.10.2.dist-info/METADATA,sha256=GDuCe5FAd2VC2rWKjkVr-QZbk9LSByY0r8YuQ13E1l8,3636
|
|
522
|
+
hatchet_sdk-1.10.2.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
523
|
+
hatchet_sdk-1.10.2.dist-info/entry_points.txt,sha256=Un_76pcLse-ZGBlwebhQpnTPyQrripeHW8J7qmEpGOk,1400
|
|
524
|
+
hatchet_sdk-1.10.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|