[PR #73] [MERGED] feat: expose internal events #93

Closed
opened 2026-02-16 02:16:33 -05:00 by yindo · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/run-llama/workflows-py/pull/73
Author: @AstraBert
Created: 8/21/2025
Status: Merged
Merged: 9/8/2025
Merged by: @AstraBert

Base: mainHead: clelia/expose-internal-events


📝 Commits (10+)

  • 15e5866 wip: expose internal events
  • ac60579 chore: add test
  • d300273 feat: handle state change
  • d425f7b feat: expose internal events
  • 3004370 fix: accept also no events as output
  • 080583e chore: implement suggestions discussed offline
  • 5c687e7 fix: adjust for 3.9
  • 59b7418 chore: implementing pr review feedback
  • c049bb5 fix: patch for unhashable dicts
  • e14f0b4 feat: refactoring and renaming according to PR review feedback

📊 Changes

9 files changed (+979 additions, -13 deletions)

View changed files

📝 docs/docs/workflows/v2/managing_events.md (+29 -0)
examples/streaming_internal_events.ipynb (+481 -0)
📝 src/workflows/context/context.py (+98 -8)
📝 src/workflows/context/state_store.py (+40 -0)
📝 src/workflows/events.py (+70 -1)
📝 src/workflows/handler.py (+6 -2)
📝 src/workflows/workflow.py (+1 -0)
📝 tests/test_state_manager.py (+73 -2)
tests/test_workflow_internal_events.py (+181 -0)

📄 Description

Closes #31

In this PR, we try to expose (through stream_events) internal processes within the workflow (step marked as in progress, step running, state of queues...)

Optimally, we should also be able to capture and report the changes in the State

Example usage:

from workflows.events import StartEvent, StopEvent, Event, InternalDispatchEvent
from workflows import Context, Workflow, step

class SecondEvent(Event):
    greeting: str

class TestWf(Workflow):
    @step
    async def step_one(self, ev: StartEvent, ctx: Context) -> SecondEvent:
        print(ev.message)
        ctx.write_event_to_stream(ev)
        return SecondEvent(greeting="hello")
    @step
    async def step_two(self, ev: SecondEvent, ctx: Context) -> StopEvent:
        print(ev.greeting)
        ctx.write_event_to_stream(ev)
        return StopEvent(result="done")

async def main():
    wf = TestWf()
    handler = wf.run(message="hi")
    async for ev in handler.stream_events(expose_internal=True):
        if isinstance(ev, InternalDispatchEvent):
            print(f"Event {type(ev.data)}: {ev.data}")

    result = await handler
    print(result)

if __name__ == "__main__":
    import asyncio

    asyncio.run(main())

Output:

Event <class 'workflows.events._QueueStateEvent'>: queue_name='_done' queue_size=1
Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_one' queue_size=1
Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_two' queue_size=1
hi
hello
Event <class 'workflows.events._InProgressStepEvent'>: ev=StartEvent() name='step_one' in_progress=True
Event <class 'workflows.events._RunningStepEvent'>: name='step_one' running=True
Event <class 'workflows.events._RunningStepEvent'>: name='step_one' running=False
Event <class 'workflows.events._InProgressStepEvent'>: ev=StartEvent() name='step_one' in_progress=False
Event <class 'workflows.events._QueueStateEvent'>: queue_name='_done' queue_size=1
Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_one' queue_size=1
Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_two' queue_size=2
Event <class 'workflows.events._InProgressStepEvent'>: ev=SecondEvent(greeting='hello') name='step_two' in_progress=True
Event <class 'workflows.events._RunningStepEvent'>: name='step_two' running=True
Event <class 'workflows.events._RunningStepEvent'>: name='step_two' running=False
Event <class 'workflows.events._InProgressStepEvent'>: ev=SecondEvent(greeting='hello') name='step_two' in_progress=False
Event <class 'workflows.events._QueueStateEvent'>: queue_name='_done' queue_size=2
Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_one' queue_size=1
Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_two' queue_size=1
Event <class 'workflows.events._InProgressStepEvent'>: ev=StopEvent() name='_done' in_progress=True
Event <class 'workflows.events._RunningStepEvent'>: name='_done' running=True
done

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/run-llama/workflows-py/pull/73 **Author:** [@AstraBert](https://github.com/AstraBert) **Created:** 8/21/2025 **Status:** ✅ Merged **Merged:** 9/8/2025 **Merged by:** [@AstraBert](https://github.com/AstraBert) **Base:** `main` ← **Head:** `clelia/expose-internal-events` --- ### 📝 Commits (10+) - [`15e5866`](https://github.com/run-llama/workflows-py/commit/15e5866cfdab0e805e788e3f69b15c9976011316) wip: expose internal events - [`ac60579`](https://github.com/run-llama/workflows-py/commit/ac605795b0128d72390beb3ef12ee78c3059a2da) chore: add test - [`d300273`](https://github.com/run-llama/workflows-py/commit/d300273b746d0c2b94c70de5bf377e4417dae04a) feat: handle state change - [`d425f7b`](https://github.com/run-llama/workflows-py/commit/d425f7b2972725c095e567c92c4a5ec39eed9423) feat: expose internal events - [`3004370`](https://github.com/run-llama/workflows-py/commit/30043701dda4fa5bfe5482b8fd14e69112070581) fix: accept also no events as output - [`080583e`](https://github.com/run-llama/workflows-py/commit/080583ed3031d5c6a43f456d817f2777bcb2ef35) chore: implement suggestions discussed offline - [`5c687e7`](https://github.com/run-llama/workflows-py/commit/5c687e723380e26d3d5c222970872a8f149a08e3) fix: adjust for 3.9 - [`59b7418`](https://github.com/run-llama/workflows-py/commit/59b7418fb5197bb536289b7f71393229048d57dd) chore: implementing pr review feedback - [`c049bb5`](https://github.com/run-llama/workflows-py/commit/c049bb50d82423d221396642c3d87f8f51d06fdc) fix: patch for unhashable dicts - [`e14f0b4`](https://github.com/run-llama/workflows-py/commit/e14f0b4f8d1a4fb1b13713a8810b779ce982239c) feat: refactoring and renaming according to PR review feedback ### 📊 Changes **9 files changed** (+979 additions, -13 deletions) <details> <summary>View changed files</summary> 📝 `docs/docs/workflows/v2/managing_events.md` (+29 -0) ➕ `examples/streaming_internal_events.ipynb` (+481 -0) 📝 `src/workflows/context/context.py` (+98 -8) 📝 `src/workflows/context/state_store.py` (+40 -0) 📝 `src/workflows/events.py` (+70 -1) 📝 `src/workflows/handler.py` (+6 -2) 📝 `src/workflows/workflow.py` (+1 -0) 📝 `tests/test_state_manager.py` (+73 -2) ➕ `tests/test_workflow_internal_events.py` (+181 -0) </details> ### 📄 Description Closes #31 In this PR, we try to expose (through stream_events) internal processes within the workflow (step marked as in progress, step running, state of queues...) Optimally, we should also be able to capture and report the changes in the State Example usage: ```python from workflows.events import StartEvent, StopEvent, Event, InternalDispatchEvent from workflows import Context, Workflow, step class SecondEvent(Event): greeting: str class TestWf(Workflow): @step async def step_one(self, ev: StartEvent, ctx: Context) -> SecondEvent: print(ev.message) ctx.write_event_to_stream(ev) return SecondEvent(greeting="hello") @step async def step_two(self, ev: SecondEvent, ctx: Context) -> StopEvent: print(ev.greeting) ctx.write_event_to_stream(ev) return StopEvent(result="done") async def main(): wf = TestWf() handler = wf.run(message="hi") async for ev in handler.stream_events(expose_internal=True): if isinstance(ev, InternalDispatchEvent): print(f"Event {type(ev.data)}: {ev.data}") result = await handler print(result) if __name__ == "__main__": import asyncio asyncio.run(main()) ``` Output: ```text Event <class 'workflows.events._QueueStateEvent'>: queue_name='_done' queue_size=1 Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_one' queue_size=1 Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_two' queue_size=1 hi hello Event <class 'workflows.events._InProgressStepEvent'>: ev=StartEvent() name='step_one' in_progress=True Event <class 'workflows.events._RunningStepEvent'>: name='step_one' running=True Event <class 'workflows.events._RunningStepEvent'>: name='step_one' running=False Event <class 'workflows.events._InProgressStepEvent'>: ev=StartEvent() name='step_one' in_progress=False Event <class 'workflows.events._QueueStateEvent'>: queue_name='_done' queue_size=1 Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_one' queue_size=1 Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_two' queue_size=2 Event <class 'workflows.events._InProgressStepEvent'>: ev=SecondEvent(greeting='hello') name='step_two' in_progress=True Event <class 'workflows.events._RunningStepEvent'>: name='step_two' running=True Event <class 'workflows.events._RunningStepEvent'>: name='step_two' running=False Event <class 'workflows.events._InProgressStepEvent'>: ev=SecondEvent(greeting='hello') name='step_two' in_progress=False Event <class 'workflows.events._QueueStateEvent'>: queue_name='_done' queue_size=2 Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_one' queue_size=1 Event <class 'workflows.events._QueueStateEvent'>: queue_name='step_two' queue_size=1 Event <class 'workflows.events._InProgressStepEvent'>: ev=StopEvent() name='_done' in_progress=True Event <class 'workflows.events._RunningStepEvent'>: name='_done' running=True done ``` --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
yindo added the pull-request label 2026-02-16 02:16:33 -05:00
yindo closed this issue 2026-02-16 02:16:33 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: run-llama/workflows-py#93