hatchet-sdk 1.9.1__py3-none-any.whl → 1.10.1__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.

Files changed (43) hide show
  1. hatchet_sdk/__init__.py +5 -1
  2. hatchet_sdk/client.py +2 -0
  3. hatchet_sdk/clients/admin.py +2 -6
  4. hatchet_sdk/clients/dispatcher/action_listener.py +43 -23
  5. hatchet_sdk/clients/events.py +58 -8
  6. hatchet_sdk/clients/rest/__init__.py +11 -0
  7. hatchet_sdk/clients/rest/api/__init__.py +1 -0
  8. hatchet_sdk/clients/rest/api/event_api.py +335 -0
  9. hatchet_sdk/clients/rest/api/filter_api.py +1305 -0
  10. hatchet_sdk/clients/rest/api/task_api.py +51 -0
  11. hatchet_sdk/clients/rest/api/workflow_runs_api.py +34 -0
  12. hatchet_sdk/clients/rest/models/__init__.py +10 -0
  13. hatchet_sdk/clients/rest/models/create_event_request.py +16 -2
  14. hatchet_sdk/clients/rest/models/v1_create_filter_request.py +99 -0
  15. hatchet_sdk/clients/rest/models/v1_event.py +142 -0
  16. hatchet_sdk/clients/rest/models/v1_event_list.py +110 -0
  17. hatchet_sdk/clients/rest/models/v1_event_workflow_run_summary.py +101 -0
  18. hatchet_sdk/clients/rest/models/v1_filter.py +127 -0
  19. hatchet_sdk/clients/rest/models/v1_filter_list.py +110 -0
  20. hatchet_sdk/clients/rest/models/v1_log_line.py +21 -2
  21. hatchet_sdk/clients/rest/models/v1_task_event.py +12 -0
  22. hatchet_sdk/clients/rest/models/v1_task_summary.py +12 -0
  23. hatchet_sdk/clients/rest/models/v1_task_timing.py +19 -0
  24. hatchet_sdk/clients/rest/models/workflow.py +5 -0
  25. hatchet_sdk/config.py +42 -0
  26. hatchet_sdk/context/context.py +1 -0
  27. hatchet_sdk/contracts/events_pb2.py +20 -20
  28. hatchet_sdk/contracts/events_pb2.pyi +14 -6
  29. hatchet_sdk/features/cron.py +1 -1
  30. hatchet_sdk/features/filters.py +181 -0
  31. hatchet_sdk/features/runs.py +7 -1
  32. hatchet_sdk/features/scheduled.py +1 -1
  33. hatchet_sdk/features/workflows.py +1 -1
  34. hatchet_sdk/hatchet.py +82 -71
  35. hatchet_sdk/opentelemetry/instrumentor.py +7 -2
  36. hatchet_sdk/runnables/standalone.py +6 -0
  37. hatchet_sdk/runnables/workflow.py +29 -2
  38. hatchet_sdk/utils/opentelemetry.py +19 -0
  39. hatchet_sdk/worker/worker.py +1 -1
  40. {hatchet_sdk-1.9.1.dist-info → hatchet_sdk-1.10.1.dist-info}/METADATA +1 -1
  41. {hatchet_sdk-1.9.1.dist-info → hatchet_sdk-1.10.1.dist-info}/RECORD +43 -34
  42. {hatchet_sdk-1.9.1.dist-info → hatchet_sdk-1.10.1.dist-info}/WHEEL +0 -0
  43. {hatchet_sdk-1.9.1.dist-info → hatchet_sdk-1.10.1.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/client.py CHANGED
@@ -5,6 +5,7 @@ from hatchet_sdk.clients.listeners.run_event_listener import RunEventListenerCli
5
5
  from hatchet_sdk.clients.listeners.workflow_listener import PooledWorkflowRunListener
6
6
  from hatchet_sdk.config import ClientConfig
7
7
  from hatchet_sdk.features.cron import CronClient
8
+ from hatchet_sdk.features.filters import FiltersClient
8
9
  from hatchet_sdk.features.logs import LogsClient
9
10
  from hatchet_sdk.features.metrics import MetricsClient
10
11
  from hatchet_sdk.features.rate_limits import RateLimitsClient
@@ -34,6 +35,7 @@ class Client:
34
35
  self.debug = debug
35
36
 
36
37
  self.cron = CronClient(self.config)
38
+ self.filters = FiltersClient(self.config)
37
39
  self.logs = LogsClient(self.config)
38
40
  self.metrics = MetricsClient(self.config)
39
41
  self.rate_limits = RateLimitsClient(self.config)
@@ -46,9 +46,7 @@ class ScheduleTriggerWorkflowOptions(BaseModel):
46
46
 
47
47
 
48
48
  class TriggerWorkflowOptions(ScheduleTriggerWorkflowOptions):
49
- additional_metadata: JSONSerializableMapping = Field(default_factory=dict)
50
49
  desired_worker_id: str | None = None
51
- namespace: str | None = None
52
50
  sticky: bool = False
53
51
  key: str | None = None
54
52
 
@@ -253,8 +251,7 @@ class AdminClient:
253
251
  try:
254
252
  namespace = options.namespace or self.namespace
255
253
 
256
- if namespace != "" and not name.startswith(self.namespace):
257
- name = f"{namespace}{name}"
254
+ name = self.config.apply_namespace(name, namespace)
258
255
 
259
256
  request = self._prepare_schedule_workflow_request(
260
257
  name, schedules, input, options
@@ -312,8 +309,7 @@ class AdminClient:
312
309
 
313
310
  namespace = options.namespace or self.namespace
314
311
 
315
- if namespace != "" and not workflow_name.startswith(self.namespace):
316
- workflow_name = f"{namespace}{workflow_name}"
312
+ workflow_name = self.config.apply_namespace(workflow_name, namespace)
317
313
 
318
314
  return self._prepare_workflow_request(workflow_name, input, trigger_options)
319
315
 
@@ -3,7 +3,7 @@ import json
3
3
  import time
4
4
  from dataclasses import field
5
5
  from enum import Enum
6
- from typing import Any, AsyncGenerator, cast
6
+ from typing import TYPE_CHECKING, Any, AsyncGenerator, cast
7
7
 
8
8
  import grpc
9
9
  import grpc.aio
@@ -18,7 +18,6 @@ from hatchet_sdk.clients.events import proto_timestamp_now
18
18
  from hatchet_sdk.clients.listeners.run_event_listener import (
19
19
  DEFAULT_ACTION_LISTENER_RETRY_INTERVAL,
20
20
  )
21
- from hatchet_sdk.config import ClientConfig
22
21
  from hatchet_sdk.connection import new_conn
23
22
  from hatchet_sdk.contracts.dispatcher_pb2 import ActionType as ActionTypeProto
24
23
  from hatchet_sdk.contracts.dispatcher_pb2 import (
@@ -32,9 +31,14 @@ from hatchet_sdk.contracts.dispatcher_pb2_grpc import DispatcherStub
32
31
  from hatchet_sdk.logger import logger
33
32
  from hatchet_sdk.metadata import get_metadata
34
33
  from hatchet_sdk.utils.backoff import exp_backoff_sleep
34
+ from hatchet_sdk.utils.opentelemetry import OTelAttribute
35
35
  from hatchet_sdk.utils.proto_enums import convert_proto_enum_to_python
36
36
  from hatchet_sdk.utils.typing import JSONSerializableMapping
37
37
 
38
+ if TYPE_CHECKING:
39
+ from hatchet_sdk.config import ClientConfig
40
+
41
+
38
42
  DEFAULT_ACTION_TIMEOUT = 600 # seconds
39
43
  DEFAULT_ACTION_LISTENER_RETRY_COUNT = 15
40
44
 
@@ -73,14 +77,27 @@ class ActionPayload(BaseModel):
73
77
  step_run_errors: dict[str, str] = Field(default_factory=dict)
74
78
  triggered_by: str | None = None
75
79
  triggers: JSONSerializableMapping = Field(default_factory=dict)
80
+ filter_payload: JSONSerializableMapping = Field(default_factory=dict)
76
81
 
77
82
  @field_validator(
78
- "input", "parents", "overrides", "user_data", "step_run_errors", mode="before"
83
+ "input",
84
+ "parents",
85
+ "overrides",
86
+ "user_data",
87
+ "step_run_errors",
88
+ "filter_payload",
89
+ mode="before",
79
90
  )
80
91
  @classmethod
81
92
  def validate_fields(cls, v: Any) -> Any:
82
93
  return v or {}
83
94
 
95
+ @model_validator(mode="after")
96
+ def validate_filter_payload(self) -> "ActionPayload":
97
+ self.filter_payload = self.triggers.get("filter_payload", {})
98
+
99
+ return self
100
+
84
101
 
85
102
  class ActionType(str, Enum):
86
103
  START_STEP_RUN = "START_STEP_RUN"
@@ -121,32 +138,35 @@ class Action(BaseModel):
121
138
  except Exception:
122
139
  return str(self.action_payload)
123
140
 
124
- @property
125
- def otel_attributes(self) -> dict[str, str | int]:
141
+ def get_otel_attributes(self, config: "ClientConfig") -> dict[str, str | int]:
126
142
  try:
127
143
  payload_str = json.dumps(self.action_payload.model_dump(), default=str)
128
144
  except Exception:
129
145
  payload_str = str(self.action_payload)
130
146
 
131
- attrs: dict[str, str | int | None] = {
132
- "hatchet.tenant_id": self.tenant_id,
133
- "hatchet.worker_id": self.worker_id,
134
- "hatchet.workflow_run_id": self.workflow_run_id,
135
- "hatchet.step_id": self.step_id,
136
- "hatchet.step_run_id": self.step_run_id,
137
- "hatchet.retry_count": self.retry_count,
138
- "hatchet.parent_workflow_run_id": self.parent_workflow_run_id,
139
- "hatchet.child_workflow_index": self.child_workflow_index,
140
- "hatchet.child_workflow_key": self.child_workflow_key,
141
- "hatchet.action_payload": payload_str,
142
- "hatchet.workflow_name": self.job_name,
143
- "hatchet.action_name": self.action_id,
144
- "hatchet.get_group_key_run_id": self.get_group_key_run_id,
145
- "hatchet.workflow_id": self.workflow_id,
146
- "hatchet.workflow_version_id": self.workflow_version_id,
147
+ attrs: dict[OTelAttribute, str | int | None] = {
148
+ OTelAttribute.TENANT_ID: self.tenant_id,
149
+ OTelAttribute.WORKER_ID: self.worker_id,
150
+ OTelAttribute.WORKFLOW_RUN_ID: self.workflow_run_id,
151
+ OTelAttribute.STEP_ID: self.step_id,
152
+ OTelAttribute.STEP_RUN_ID: self.step_run_id,
153
+ OTelAttribute.RETRY_COUNT: self.retry_count,
154
+ OTelAttribute.PARENT_WORKFLOW_RUN_ID: self.parent_workflow_run_id,
155
+ OTelAttribute.CHILD_WORKFLOW_INDEX: self.child_workflow_index,
156
+ OTelAttribute.CHILD_WORKFLOW_KEY: self.child_workflow_key,
157
+ OTelAttribute.ACTION_PAYLOAD: payload_str,
158
+ OTelAttribute.WORKFLOW_NAME: self.job_name,
159
+ OTelAttribute.ACTION_NAME: self.action_id,
160
+ OTelAttribute.GET_GROUP_KEY_RUN_ID: self.get_group_key_run_id,
161
+ OTelAttribute.WORKFLOW_ID: self.workflow_id,
162
+ OTelAttribute.WORKFLOW_VERSION_ID: self.workflow_version_id,
147
163
  }
148
164
 
149
- return {k: v for k, v in attrs.items() if v}
165
+ return {
166
+ f"hatchet.{k.value}": v
167
+ for k, v in attrs.items()
168
+ if v and k not in config.otel.excluded_attributes
169
+ }
150
170
 
151
171
  @property
152
172
  def key(self) -> ActionKey:
@@ -172,7 +192,7 @@ def parse_additional_metadata(additional_metadata: str) -> JSONSerializableMappi
172
192
 
173
193
 
174
194
  class ActionListener:
175
- def __init__(self, config: ClientConfig, worker_id: str) -> None:
195
+ def __init__(self, config: "ClientConfig", worker_id: str) -> None:
176
196
  self.config = config
177
197
  self.worker_id = worker_id
178
198
 
@@ -6,7 +6,12 @@ from typing import List, cast
6
6
  from google.protobuf import timestamp_pb2
7
7
  from pydantic import BaseModel, Field
8
8
 
9
+ from hatchet_sdk.clients.rest.api.event_api import EventApi
10
+ from hatchet_sdk.clients.rest.api.workflow_runs_api import WorkflowRunsApi
11
+ from hatchet_sdk.clients.rest.api_client import ApiClient
12
+ from hatchet_sdk.clients.rest.models.v1_event_list import V1EventList
9
13
  from hatchet_sdk.clients.rest.tenacity_utils import tenacity_retry
14
+ from hatchet_sdk.clients.v1.api_client import BaseRestClient
10
15
  from hatchet_sdk.config import ClientConfig
11
16
  from hatchet_sdk.connection import new_conn
12
17
  from hatchet_sdk.contracts.events_pb2 import (
@@ -33,6 +38,8 @@ def proto_timestamp_now() -> timestamp_pb2.Timestamp:
33
38
  class PushEventOptions(BaseModel):
34
39
  additional_metadata: JSONSerializableMapping = Field(default_factory=dict)
35
40
  namespace: str | None = None
41
+ priority: int | None = None
42
+ scope: str | None = None
36
43
 
37
44
 
38
45
  class BulkPushEventOptions(BaseModel):
@@ -43,16 +50,26 @@ class BulkPushEventWithMetadata(BaseModel):
43
50
  key: str
44
51
  payload: JSONSerializableMapping = Field(default_factory=dict)
45
52
  additional_metadata: JSONSerializableMapping = Field(default_factory=dict)
53
+ priority: int | None = None
54
+ scope: str | None = None
46
55
 
47
56
 
48
- class EventClient:
57
+ class EventClient(BaseRestClient):
49
58
  def __init__(self, config: ClientConfig):
59
+ super().__init__(config)
60
+
50
61
  conn = new_conn(config, False)
51
- self.client = EventsServiceStub(conn)
62
+ self.events_service_client = EventsServiceStub(conn)
52
63
 
53
64
  self.token = config.token
54
65
  self.namespace = config.namespace
55
66
 
67
+ def _wra(self, client: ApiClient) -> WorkflowRunsApi:
68
+ return WorkflowRunsApi(client)
69
+
70
+ def _ea(self, client: ApiClient) -> EventApi:
71
+ return EventApi(client)
72
+
56
73
  async def aio_push(
57
74
  self,
58
75
  event_key: str,
@@ -79,7 +96,7 @@ class EventClient:
79
96
  options: PushEventOptions = PushEventOptions(),
80
97
  ) -> Event:
81
98
  namespace = options.namespace or self.namespace
82
- namespaced_event_key = namespace + event_key
99
+ namespaced_event_key = self.client_config.apply_namespace(event_key, namespace)
83
100
 
84
101
  try:
85
102
  meta_bytes = json.dumps(options.additional_metadata)
@@ -96,16 +113,21 @@ class EventClient:
96
113
  payload=payload_str,
97
114
  eventTimestamp=proto_timestamp_now(),
98
115
  additionalMetadata=meta_bytes,
116
+ priority=options.priority,
117
+ scope=options.scope,
99
118
  )
100
119
 
101
- return cast(Event, self.client.Push(request, metadata=get_metadata(self.token)))
120
+ return cast(
121
+ Event,
122
+ self.events_service_client.Push(request, metadata=get_metadata(self.token)),
123
+ )
102
124
 
103
125
  def _create_push_event_request(
104
126
  self,
105
127
  event: BulkPushEventWithMetadata,
106
128
  namespace: str,
107
129
  ) -> PushEventRequest:
108
- event_key = namespace + event.key
130
+ event_key = self.client_config.apply_namespace(event.key, namespace)
109
131
  payload = event.payload
110
132
 
111
133
  meta = event.additional_metadata
@@ -125,6 +147,8 @@ class EventClient:
125
147
  payload=serialized_payload,
126
148
  eventTimestamp=proto_timestamp_now(),
127
149
  additionalMetadata=meta_str,
150
+ priority=event.priority,
151
+ scope=event.scope,
128
152
  )
129
153
 
130
154
  ## IMPORTANT: Keep this method's signature in sync with the wrapper in the OTel instrumentor
@@ -145,7 +169,9 @@ class EventClient:
145
169
  return list(
146
170
  cast(
147
171
  Events,
148
- self.client.BulkPush(bulk_request, metadata=get_metadata(self.token)),
172
+ self.events_service_client.BulkPush(
173
+ bulk_request, metadata=get_metadata(self.token)
174
+ ),
149
175
  ).events
150
176
  )
151
177
 
@@ -157,7 +183,7 @@ class EventClient:
157
183
  message=message,
158
184
  )
159
185
 
160
- self.client.PutLog(request, metadata=get_metadata(self.token))
186
+ self.events_service_client.PutLog(request, metadata=get_metadata(self.token))
161
187
 
162
188
  @tenacity_retry
163
189
  def stream(self, data: str | bytes, step_run_id: str) -> None:
@@ -174,4 +200,28 @@ class EventClient:
174
200
  message=data_bytes,
175
201
  )
176
202
 
177
- self.client.PutStreamEvent(request, metadata=get_metadata(self.token))
203
+ self.events_service_client.PutStreamEvent(
204
+ request, metadata=get_metadata(self.token)
205
+ )
206
+
207
+ async def aio_list(
208
+ self,
209
+ offset: int | None = None,
210
+ limit: int | None = None,
211
+ keys: list[str] | None = None,
212
+ ) -> V1EventList:
213
+ return await asyncio.to_thread(self.list, offset=offset, limit=limit, keys=keys)
214
+
215
+ def list(
216
+ self,
217
+ offset: int | None = None,
218
+ limit: int | None = None,
219
+ keys: list[str] | None = None,
220
+ ) -> V1EventList:
221
+ with self.client() as client:
222
+ return self._ea(client).v1_event_list(
223
+ tenant=self.client_config.tenant_id,
224
+ offset=offset,
225
+ limit=limit,
226
+ keys=keys,
227
+ )
@@ -20,6 +20,7 @@ __version__ = "1.0.0"
20
20
  from hatchet_sdk.clients.rest.api.api_token_api import APITokenApi
21
21
  from hatchet_sdk.clients.rest.api.default_api import DefaultApi
22
22
  from hatchet_sdk.clients.rest.api.event_api import EventApi
23
+ from hatchet_sdk.clients.rest.api.filter_api import FilterApi
23
24
  from hatchet_sdk.clients.rest.api.github_api import GithubApi
24
25
  from hatchet_sdk.clients.rest.api.healthcheck_api import HealthcheckApi
25
26
  from hatchet_sdk.clients.rest.api.log_api import LogApi
@@ -229,7 +230,17 @@ from hatchet_sdk.clients.rest.models.user_tenant_memberships_list import (
229
230
  )
230
231
  from hatchet_sdk.clients.rest.models.user_tenant_public import UserTenantPublic
231
232
  from hatchet_sdk.clients.rest.models.v1_cancel_task_request import V1CancelTaskRequest
233
+ from hatchet_sdk.clients.rest.models.v1_create_filter_request import (
234
+ V1CreateFilterRequest,
235
+ )
232
236
  from hatchet_sdk.clients.rest.models.v1_dag_children import V1DagChildren
237
+ from hatchet_sdk.clients.rest.models.v1_event import V1Event
238
+ from hatchet_sdk.clients.rest.models.v1_event_list import V1EventList
239
+ from hatchet_sdk.clients.rest.models.v1_event_workflow_run_summary import (
240
+ V1EventWorkflowRunSummary,
241
+ )
242
+ from hatchet_sdk.clients.rest.models.v1_filter import V1Filter
243
+ from hatchet_sdk.clients.rest.models.v1_filter_list import V1FilterList
233
244
  from hatchet_sdk.clients.rest.models.v1_log_line import V1LogLine
234
245
  from hatchet_sdk.clients.rest.models.v1_log_line_level import V1LogLineLevel
235
246
  from hatchet_sdk.clients.rest.models.v1_log_line_list import V1LogLineList
@@ -4,6 +4,7 @@
4
4
  from hatchet_sdk.clients.rest.api.api_token_api import APITokenApi
5
5
  from hatchet_sdk.clients.rest.api.default_api import DefaultApi
6
6
  from hatchet_sdk.clients.rest.api.event_api import EventApi
7
+ from hatchet_sdk.clients.rest.api.filter_api import FilterApi
7
8
  from hatchet_sdk.clients.rest.api.github_api import GithubApi
8
9
  from hatchet_sdk.clients.rest.api.healthcheck_api import HealthcheckApi
9
10
  from hatchet_sdk.clients.rest.api.log_api import LogApi