-
Notifications
You must be signed in to change notification settings - Fork 24
[wip] Events API #279
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
kumare3
wants to merge
11
commits into
main
Choose a base branch
from
conditions
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
[wip] Events API #279
Changes from 7 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
28ca206
[wip] Events API
kumare3 791c54d
more wip
kumare3 72e2051
assignment fixed
kumare3 1dc01b3
Merge branch 'main' into conditions
kumare3 9193283
updated
kumare3 e61f89c
Merge branch 'main' into conditions
kumare3 58fb4a8
Merge branch 'main' into conditions
kumare3 1724b43
Merge branch 'main' into conditions
kumare3 27c9c3e
Merge branch 'main' into conditions
kumare3 516ac61
Merge branch 'main' into conditions
kumare3 565358c
merged and cleaned with tui
kumare3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| import flyte | ||
|
|
||
| env = flyte.TaskEnvironment(name="events") | ||
|
|
||
|
|
||
| @env.task | ||
| async def next_task(value: int) -> int: | ||
| return value + 1 | ||
|
|
||
|
|
||
| @env.task | ||
| async def my_task(x: int) -> int: | ||
| event1 = await flyte.new_event.aio( | ||
| "my_event", | ||
| scope="run", | ||
| prompt="Is it ok to continue?", | ||
| data_type=bool, | ||
| ) | ||
| event2 = await flyte.new_event.aio( | ||
| "proceed_event", | ||
| scope="run", | ||
| prompt="What should I add to x?", | ||
| data_type=int, | ||
| ) | ||
| event3 = await flyte.new_event.aio( | ||
| "final_event", | ||
| scope="run", | ||
| prompt="What should I return if the first event was negative?", | ||
| data_type=int, | ||
| ) | ||
| result = await event1.wait.aio() | ||
| if result: | ||
| print("Event signaled positive response, proceeding to next_task", flush=True) | ||
| result2 = await event2.wait.aio() | ||
| return await next_task(x + result2) | ||
| else: | ||
| print("Event signaled negative response, returning -1", flush=True) | ||
| result3 = await event3.wait.aio() | ||
| return result3 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| flyte.init() | ||
|
|
||
| r = flyte.run(my_task, x=10) | ||
| print(r.url) | ||
| print(r.outputs()) | ||
|
|
||
| import flyte.remote as remote | ||
|
|
||
| while not (remote_event := remote.Event.get("my_event", r.name)): | ||
| time.sleep(10) | ||
|
|
||
| remote_event.signal(True) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| import typing | ||
| from dataclasses import dataclass | ||
| from typing import Generic, Literal, Type | ||
|
|
||
| import rich.repr | ||
|
|
||
| from flyte.syncify import syncify | ||
|
|
||
| EventScope = Literal["task", "run", "action"] | ||
|
|
||
| EventType = typing.TypeVar("EventType", bool, int, float, str) | ||
|
|
||
|
|
||
| @rich.repr.auto | ||
| @dataclass | ||
| class _Event(Generic[EventType]): | ||
| """ | ||
| An event that can be awaited in a Run. Events can be used to pause Run until an external signal is received. | ||
|
|
||
| Examples: | ||
|
|
||
| ```python | ||
| import flyte | ||
|
|
||
| env = flyte.TaskEnvironment(name="events") | ||
|
|
||
| @env.task | ||
| async def my_task() -> Optional[int]: | ||
| event = await flyte.new_event(name="my_event", scope="run", prompt="Is it ok to continue?", data_type=bool) | ||
| result = await event.wait() | ||
| if result: | ||
| return 42 | ||
| else: | ||
| return None | ||
| ``` | ||
| """ | ||
|
|
||
| name: str | ||
| # TODO restrict scope to action only right now | ||
| scope: EventScope = "run" | ||
| # TODO Support prompt as html | ||
| prompt: str = "Approve?" | ||
| data_type: Type[EventType] = bool # type: ignore[assignment] | ||
| description: str = "" | ||
|
|
||
| def __post_init__(self): | ||
| valid_types = (bool, int, float, str) | ||
| if self.data_type not in valid_types: | ||
| raise TypeError(f"Invalid data_type {self.data_type}. Must be one of {valid_types}.") | ||
|
|
||
| @syncify | ||
| async def wait(self) -> EventType: | ||
| """ | ||
| Await the event to be signaled. | ||
|
|
||
| :return: The payload associated with the event when it is signaled. | ||
| """ | ||
| from flyte._context import internal_ctx | ||
|
|
||
| ctx = internal_ctx() | ||
| if ctx.is_task_context(): | ||
| # If we are in a task context, that implies we are executing a Run. | ||
| # In this scenario, we should submit the task to the controller. | ||
| # We will also check if we are not initialized, It is not expected to be not initialized | ||
| from ._internal.controllers import get_controller | ||
|
|
||
| controller = get_controller() | ||
| result = await controller.wait_for_event(self) | ||
| return result | ||
| else: | ||
| raise RuntimeError("Events can only be awaited within a task context.") | ||
|
|
||
|
|
||
| @syncify | ||
| async def new_event( | ||
| name: str, | ||
| /, | ||
| scope: EventScope = "run", | ||
| prompt: str = "Approve?", | ||
| data_type: Type[EventType] = bool, # type: ignore[assignment] | ||
| description: str = "", | ||
| ) -> _Event: | ||
| """ | ||
| Create an event that can be awaited in a workflow. Events can be used to pause workflow execution until | ||
| an external signal is received. | ||
|
|
||
| :param name: Name of the event | ||
| :param scope: Scope of the event - "task", "run", or "action" | ||
| :param prompt: Prompt message for the event | ||
| :param data_type: Data type of the event payload | ||
| :param description: Description of the event | ||
| :return: An instance of _Event representing the created event | ||
| """ | ||
| event = _Event(name=name, scope=scope, prompt=prompt, data_type=data_type, description=description) | ||
| from flyte._context import internal_ctx | ||
|
|
||
| ctx = internal_ctx() | ||
| if ctx.is_task_context(): | ||
| # If we are in a task context, that implies we are executing a Run. | ||
| # In this scenario, we should submit the task to the controller. | ||
| # We will also check if we are not initialized, It is not expected to be not initialized | ||
| from ._internal.controllers import get_controller | ||
|
|
||
| controller = get_controller() | ||
| await controller.register_event(event) | ||
| else: | ||
| pass | ||
| return event |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should allow users to make the prompt fancy, like html etc (reports)
And then we should also have an even type that is a webhook invocation with a callback to signal