Skip to content

Feat: Wait wrapper signature improvements#3241

Merged
mrkaye97 merged 29 commits intofeat-durable-executionfrom
mk/sdk-sig-cleanup
Mar 12, 2026
Merged

Feat: Wait wrapper signature improvements#3241
mrkaye97 merged 29 commits intofeat-durable-executionfrom
mk/sdk-sig-cleanup

Conversation

@mrkaye97
Copy link
Contributor

@mrkaye97 mrkaye97 commented Mar 11, 2026

Description

Working on improving wrapper signatures on the SDK (wait for event, sleep for, ...) to return something helpful instead of the internal event which is returned now.

Also implemented the memoized now() method on the durable context in Python, and removed the Restore button from the UI

Type of change

  • New feature (non-breaking change which adds functionality)

@vercel
Copy link

vercel bot commented Mar 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hatchet-docs Ready Ready Preview, Comment Mar 12, 2026 4:30pm

Request Review

@mrkaye97 mrkaye97 changed the title Mk/sdk sig cleanup Feat: Wait wrapper signature improvements Mar 11, 2026
@promptless-for-oss
Copy link

📝 Documentation updates detected!

New suggestion: Document aio_wait_for_event() method and typed return models for Python SDK


Tip: Use labels in the Promptless dashboard to categorize suggestions by release or team 🏷️

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Hatchet durable SDK “wait” helpers to return structured, user-friendly results (sleep duration + event metadata) instead of raw internal callback payloads, adds a memoized “now” helper for durable contexts, and aligns backend/UI/examples with the new semantics.

Changes:

  • Add higher-level durable context helpers (waitForEvent, structured sleepFor, memoized now) and update SDK examples to use them.
  • Extend backend matching/callback payloads to include richer user-event data (id, tenant, seen_at, scope, additional metadata, decoded payload).
  • Remove the “Restore” button from the workflow run UI and update Python tests/examples accordingly.

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
sdks/typescript/src/v1/client/worker/context.ts Adds SleepResult, HatchetEvent, waitForEvent, memoized now, and changes sleepFor return type.
sdks/typescript/src/v1/examples/durable/workflow.ts Updates durable example to use new sleepFor/waitForEvent outputs and adds memoNowCaching example.
sdks/typescript/src/v1/examples/durable_event/workflow.ts Updates durable-event examples to use waitForEvent (with optional CEL filter).
sdks/typescript/src/v1/examples/durable_eviction/workflow.ts Updates eviction example to use waitForEvent.
examples/typescript/durable/workflow.ts Mirrors TypeScript SDK examples with new helper signatures and memoized now.
examples/typescript/durable_event/workflow.ts Mirrors TypeScript durable-event examples with waitForEvent.
examples/typescript/durable_eviction/workflow.ts Mirrors TypeScript eviction example with waitForEvent.
sdks/python/hatchet_sdk/context/context.py Adds Event/SleepResult models, wrappers aio_sleep_for/aio_wait_for_event, memoized aio_now, and renames memo helper.
sdks/python/hatchet_sdk/utils/timedelta_to_expression.py Adds expr_to_timedelta utility to parse duration expressions.
sdks/python/tests/test_durations.py Adds unit tests for expr_to_timedelta.
sdks/python/examples/durable/worker.py Updates Python durable examples to use the new wrappers and adds memoized aio_now example.
sdks/python/examples/durable_event/worker.py Updates durable-event example to use aio_wait_for_event.
sdks/python/examples/durable_eviction/worker.py Updates eviction example to use aio_wait_for_event.
sdks/python/examples/worker.py Registers the new memo_now_caching durable task example.
sdks/python/examples/durable/test_durable.py Updates durable tests for async event push + new outputs and adds memo-now replay assertion.
sdks/guides/python/human_in_the_loop/worker.py Updates guide to use aio_wait_for_event helper.
examples/python/durable/worker.py Mirrors Python SDK examples with new helper signatures.
examples/python/durable_event/worker.py Mirrors Python durable-event example using aio_wait_for_event.
examples/python/durable_eviction/worker.py Mirrors Python eviction example using aio_wait_for_event.
examples/python/worker.py Registers the new memo_now_caching example task.
examples/python/guides/human_in_the_loop/worker.py Mirrors guide update to aio_wait_for_event.
sdks/python/poetry.lock Updates Python lockfile (Poetry-generated metadata and dependency markers).
pkg/repository/trigger.go Adds JSON tags to event trigger option struct fields.
pkg/repository/match.go Expands candidate matches and marshals a richer matched-event JSON payload for user events.
pkg/repository/durable_events.go Propagates wait-for callback result payload through ingest results.
internal/services/dispatcher/v1/server.go Passes wait-for result payload through callback completion delivery.
internal/services/controllers/task/controller.go Populates candidate match fields for user event scope + additional metadata.
frontend/app/src/pages/main/v1/workflow-runs-v1/$run/v2components/step-run-detail/step-run-detail.tsx Removes Restore button and related query/mutation logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +26 to +29
def expr_to_timedelta(expr: str) -> timedelta:
unit = expr[-1]
value = int(expr[:-1])

Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expr_to_timedelta only supports single-unit expressions like "10m" by slicing the last character as the unit. But Duration also allows arbitrary strings, and the engine uses Go-style duration strings (e.g. "1h30m", "1h30m5s") in several places. This implementation will raise ValueError for those valid durations; consider parsing the full Go-style h/m/s format (and keeping behavior consistent with the "engine only supports hours/minutes/seconds" comment).

Copilot uses AI. Check for mistakes.
APPROVAL_EVENT_KEY,
f"input.runId == '{run_id}'",
)
return approval
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aio_wait_for_event now returns an Event model, but this helper returns it directly while downstream code in this guide treats the result like a dict (e.g. approval.get(...)). To keep the guide working, either return approval.payload here (so callers receive the event body), or update the rest of the guide to use approval.payload[...] / approval.payload.get(...).

Suggested change
return approval
return approval.payload

Copilot uses AI. Check for mistakes.
APPROVAL_EVENT_KEY,
f"input.runId == '{run_id}'",
)
return approval
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aio_wait_for_event now returns an Event model, but this helper returns it directly while downstream code in this example treats the result like a dict (e.g. approval.get(...)). Either return approval.payload here or update the rest of the example to use approval.payload.get(...) so it doesn’t fail at runtime.

Suggested change
return approval
return approval.payload

Copilot uses AI. Check for mistakes.
Comment on lines +144 to 147
"runtime": time.time() - start,
"key": key,
"event_id": event_id,
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait_for_or_group_2 is annotated to return dict[str, str | int], but runtime is now a float (time.time() - start). This will fail mypy under the SDK’s strict settings. Update the return annotation to include float (or cast/round the runtime back to int).

Copilot uses AI. Check for mistakes.
Comment on lines +852 to +856
now = await self._aio_memo(
self._now,
MemoNowResult,
)

Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aio_now uses _aio_memo(self._now, MemoNowResult) with no args/kwargs, but _compute_memo_key only hashes the task_run_external_id + args/kwargs (it does not include the function identity). This makes the memo key collide with any other memoization call that also has no args/kwargs, potentially returning the wrong cached payload/type. Consider namespacing the key (e.g. pass a constant arg like 'now', or include fn.__qualname__ in the memo key computation).

Copilot uses AI. Check for mistakes.
async now(): Promise<Date> {
const result = await this.memo(async () => {
return { ts: new Date().toISOString() };
}, []);
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now() memoizes with deps: []. That means it shares the same memo key as any other ctx.memo(fn, []) call in the same task run, which can lead to collisions and type/shape mismatches when reading memoized payloads. Consider including a reserved dep value (e.g. a constant like 'now') so the memo key is namespaced per built-in helper.

Suggested change
}, []);
}, ['now']);

Copilot uses AI. Check for mistakes.
Comment on lines +141 to 144
"runtime": time.time() - start,
"key": key,
"event_id": event_id,
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait_for_or_group_2 is annotated to return dict[str, str | int], but runtime is now a float (time.time() - start). This will fail type checking if these examples are included. Update the return annotation to include float (or cast/round the runtime back to int).

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

}
}

return { durationMs: durationToMs(duration) };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might be worth a conversation....

@mrkaye97 mrkaye97 merged commit 6fb50ce into feat-durable-execution Mar 12, 2026
52 checks passed
@mrkaye97 mrkaye97 deleted the mk/sdk-sig-cleanup branch March 12, 2026 16:53
@promptless-for-oss
Copy link

📝 Documentation updates detected!

New suggestion: Document TypeScript DurableContext methods: waitForEvent, now, sleepUntil, and SleepResult


Tip: Request one-off documentation tasks in the Dashboard under New Task 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants