Skip to content

Add high-level schedule() API for scheduling functions#397

Open
flossypurse wants to merge 7 commits intomainfrom
feature/high-level-schedule-api
Open

Add high-level schedule() API for scheduling functions#397
flossypurse wants to merge 7 commits intomainfrom
feature/high-level-schedule-api

Conversation

@flossypurse
Copy link
Contributor

@flossypurse flossypurse commented Feb 16, 2026

Summary

This PR implements a clean, high-level API for scheduling functions to run periodically using cron expressions. Closes #328.

✨ Now rebased on main with the new simplified client init from #386.

Problem

Previously, users had to use the low-level schedules API which required many parameters and was complex to use.

Solution

This PR adds a simple, high-level schedule() method that handles all the complexity:

# New high-level API (simple!) - works with new Resonate() constructor
resonate = Resonate()  # Local mode
# or
resonate = Resonate(url="http://localhost:8001")  # Remote mode

resonate.schedule("my_schedule", my_function, "0 9 * * *", arg1, arg2)

Key Features

  1. Simple API: resonate.schedule(id, func, cron, *args, **kwargs)
  2. Function method: function.schedule(id, cron, *args, **kwargs) for registered functions
  3. Options support: Works with .options() for custom timeout, tags, etc.
  4. Auto-generation: Automatically generates promise IDs and handles encoding
  5. Sensible defaults: Timeout defaults to 24 hours
  6. Works with new API: Compatible with the simplified Resonate() constructor from Simplify client init, add token auth, clarify local routing #386

Examples

Basic scheduling

@resonate.register
def generate_report(ctx: Context, user_id: int) -> str:
    return f"Generated report for user {user_id}"

# Schedule to run every day at 9am
schedule = resonate.schedule(
    "daily_report",
    generate_report,
    "0 9 * * *",
    user_id=123
)

With options

schedule = resonate.options(
    timeout=3600,
    tags={"env": "production"}
).schedule(
    "priority_sync",
    sync_data,
    "*/30 * * * *",
    source="api"
)

Function-level scheduling

@resonate.register
def cleanup(ctx: Context, days: int):
    ...

cleanup.schedule("nightly_cleanup", "0 2 * * *", days=30)

Changes

  • ✅ Added schedule() method to Resonate class
  • ✅ Added schedule() method to Function class
  • ✅ Uses new Resonate() constructor (no more .local() or .remote())
  • ✅ Comprehensive test suite (tests/test_schedule_api.py)
  • ✅ Example script demonstrating usage (example_schedule.py)
  • ✅ README documentation with examples
  • ✅ Full docstrings with examples

Testing

The implementation has been tested with:

  • Local store testing
  • Example script validation

Run tests:

python example_schedule.py  # Demonstrates all features

Breaking Changes

None. This is a purely additive change that wraps the existing low-level API.

Related Issues

Closes #328


🤖 Generated with Claude Code

flossypurse added a commit to resonatehq/resonate-sdk-ts that referenced this pull request Feb 16, 2026
This commit enhances the existing schedule() method and fixes critical bugs:

Bug fixes:
- Fixed promise ID pattern to include function name (was missing)
- Fixed resonate:invoke tag to use function name instead of opts.target
- Pattern is now: `{scheduleId}.{{.timestamp}}.{functionName}`

Enhancements:
- Added schedule() method to ResonateFunc interface
- Registered functions can now call .schedule() directly
- Comprehensive documentation with examples
- Example script demonstrating all usage patterns

Examples:
    // On Resonate class
    await resonate.schedule("daily", "0 9 * * *", myFunc, arg1);

    // On registered function
    const func = resonate.register("myFunc", async (ctx, x) => x);
    await func.schedule("daily", "0 9 * * *", arg1);

    // With options
    await resonate
      .options({ timeout: 3600000, tags: { env: "prod" } })
      .schedule("sync", "*/30 * * * *", syncFunc);

The implementation now matches the Python SDK's pattern and provides
a consistent developer experience across both SDKs.

Related:
- TypeScript issue: #435
- Python SDK PR: resonatehq/resonate-sdk-py#397
- Python SDK issue: resonatehq/resonate-sdk-py#328

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
flossypurse and others added 2 commits February 16, 2026 11:02
This commit implements a clean, high-level API for scheduling functions
to run periodically using cron expressions. This addresses issue #328.

Key features:
- Simple API: resonate.schedule(id, func, cron, *args, **kwargs)
- Function method: function.schedule(id, cron, *args, **kwargs)
- Supports all existing options (timeout, tags, etc.) via .options()
- Auto-generates promise IDs and handles encoding
- Sensible defaults for timeout (24 hours)

Examples:
    # Basic scheduling
    resonate.schedule("daily_report", generate_report, "0 9 * * *", user_id=123)

    # With options
    resonate.options(timeout=3600, tags={"env": "prod"}).schedule(...)

    # Function-level scheduling
    @resonate.register
    def report(ctx: Context, user_id: int):
        ...

    report.schedule("daily", "0 9 * * *", user_id=123)

The implementation wraps the low-level schedules API and provides
a developer-friendly interface similar to run() and rpc().

Includes:
- Implementation in resonate.py (Resonate and Function classes)
- Comprehensive test suite in tests/test_schedule_api.py
- Example script demonstrating usage
- README documentation with examples

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@flossypurse flossypurse force-pushed the feature/high-level-schedule-api branch from fe83956 to ce7549c Compare February 16, 2026 18:20
flossypurse and others added 2 commits February 16, 2026 11:59
- Add future annotations import to example_schedule.py
- Add noqa: T201 to all print statements in example
- Move Schedule import to TYPE_CHECKING block in resonate.py
- Fix type: ignore comments to specify arg-type rule
- Use constant variable for pytest.mark.skipif condition
- Fix docstrings to imperative mood

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Examples go in resonatehq-examples org, not in the SDK repo.
A dedicated example app will be created there.

Related to #397

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs in the Resonate.schedule() implementation:

1. resonate:invoke tag was set to the function name instead of the
   routing target (e.g. poll://any@default). Fixed to match how
   rpc() routes tasks using opts.target.

2. Encoded promise data was missing "func" and "version" fields
   required by the bridge to dispatch executions. Fixed to include
   all fields the bridge expects: func, args, kwargs, version.

Both issues prevented scheduled promises from being picked up and
executed by workers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@flossypurse
Copy link
Contributor Author

Testing complete — ran the example app end-to-end against the local feature branch. Found and fixed two bugs:

Bug 1: Wrong resonate:invoke tag

resonate:invoke was set to the function name (e.g. generate_report) instead of the routing target. This meant the server couldn't route tasks to the correct worker group.

Fix: Use poll://any@{target} format, consistent with how rpc() routes tasks via the Remote convention.

Bug 2: Missing func/version in encoded promise data

The bridge asserts "func" in data and "version" in data when processing task invocations, but schedule() was only encoding {"args": ..., "kwargs": ...}.

Fix: Encode {"func": name, "args": list(args), "kwargs": kwargs, "version": version} — matching the format the bridge expects.

Example apps

Both verified end-to-end: scheduled promises created, picked up by workers, resolved correctly.

flossypurse and others added 2 commits February 18, 2026 18:11
The schedule API should only be accessible via the Resonate client (resonate.schedule()), not on registered Function objects. Remove Function.schedule() method and corresponding tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@flossypurse flossypurse requested review from Tomperez98 and dfarr and removed request for Tomperez98 February 20, 2026 16:14
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.

Provide a high level API for scheduling functions.

1 participant