Event Emitter
What is EventEmitter?
EventEmitter is a small orchestration primitive that sends typed events through a configurable chain of hooks and handlers.
It accepts values (text, dicts, or
Eventinstances) and wraps them intoEventobjects when needed.It enforces a minimum severity level (
event_level) so only important events flow through.It applies one or more hooks that can transform or enrich events.
It forwards the final event to a list of handlers (console, print, stream, or custom).
It optionally supports streaming consumption when a
StreamEventHandleris attached.
In GLLM Core, components like Component.run() use EventEmitter to emit structured telemetry instead of ad‑hoc print or logging calls.
Installation
# you can use a Conda environment
pip install --extra-index-url https://oauth2accesstoken:$(gcloud auth print-access-token)@glsdk.gdplabs.id/gen-ai-internal/simple/ gllm-core# you can use a Conda environment
pip install --extra-index-url https://oauth2accesstoken:$(gcloud auth print-access-token)@glsdk.gdplabs.id/gen-ai-internal/simple/ gllm-core# you can use a Conda environment
FOR /F "tokens=*" %T IN ('gcloud auth print-access-token') DO pip install --extra-index-url "https://oauth2accesstoken:%T@glsdk.gdplabs.id/gen-ai-internal/simple/" "gllm-core"Quickstart
A basic console setup:
What happens:
with_console_handlercreates anEventEmitterwith a singleConsoleEventHandler.The emitter is configured with a minimum severity of
INFO.emit("Hello, world!", event_level=EventLevel.INFO)constructs anEvent(if not already one).The event passes the severity check (INFO ≥ INFO) and is forwarded to the handler.
close()gracefully shuts down all handlers.
Constructing an EventEmitter
There are two main ways to construct an EventEmitter.
Direct constructor
handlersmust be a non-empty list of objects implementingBaseEventHandler.event_leveldefines the minimumEventLevelthat will be processed.hooksis an optional list ofBaseEventHookinstances applied before handlers.An empty
handlerslist raisesValueError.
Factory constructors
EventEmitter.with_console_handler(event_level=..., hooks=...)EventEmitter.with_print_handler(event_level=..., hooks=...)EventEmitter.with_stream_handler(event_level=..., hooks=...)
Each factory:
Creates a single appropriate handler (
ConsoleEventHandler,PrintEventHandler, orStreamEventHandler).Applies the provided
event_levelandhooks.Returns a ready-to-use
EventEmitterinstance.
Emitting Events
The core method is emit:
Key behaviors:
Input value forms
If
valueis already anEventinstance, it is used directly and the rest of the parameters are ignored (except for the severity check).Otherwise,
EventEmittercreates a newEvent(id, value, level, type, metadata, timestamp).
Severity filtering
event_levelis validated againstEventLevelusingvalidate_string_enum.If the event’s level is below the emitter’s
severity, the method returns early and nothing is emitted.
Hook application
Each hook in
self.hooksis awaited sequentially.Each hook receives the current
Eventand must return a new or modifiedEvent.The final
Eventinstance is what handlers will see.
Handler fan-out
disabled_handlersis an optional list of handler names to skip.For each handler in
self.handlers:If
handler.nameis not indisabled_handlers,await handler.emit(event)is called.Handlers are responsible for output (printing, logging, streaming, etc.).
Configuring Severity Thresholds
The event_level argument in the constructor or factory methods determines the minimum severity that will be processed.
The emitter stores this threshold as
self.severity.Each call to
emitcompares the event’s level with this threshold.If
event_level < self.severity, the event is ignored.
Practical usage patterns:
Use
EventLevel.DEBUGduring development to see all events.Use
EventLevel.INFOin staging environments.Use
EventLevel.WARNINGor higher in noisy production paths.
Using Hooks
Hooks are a way to transform events before they reach handlers.
Hooks implement the
BaseEventHookinterface and are awaited one by one.Each hook receives an
Eventand returns a (possibly) newEvent.Examples of what hooks can do:
Serialize complex values into JSON strings.
Redact sensitive fields from
metadata.Add correlation IDs or request IDs to
metadata.
Attach hooks when constructing an emitter:
Choosing Handlers
Handlers are responsible for what actually happens when an event is emitted.
ConsoleEventHandler
Designed to write formatted events to the console.
Useful for local development and rich CLI output.
PrintEventHandler
Uses simple
print-style output.Ideal for environments where you only need plain text logs (e.g., simple scripts, tests).
StreamEventHandler
Queues events so they can be iterated over via
EventEmitter.stream().Designed for streaming use cases (e.g., yielding events to a client or UI).
You can also implement custom handlers by subclassing BaseEventHandler and passing instances via the handlers argument.
Streaming Events
EventEmitter supports streaming when configured with a StreamEventHandler.
EventEmitter.with_stream_handler()creates an emitter with exactly oneStreamEventHandler.EventEmitter.stream()locates that handler and returns its async generator.If there is not exactly one
StreamEventHandler,stream()raisesValueError.
Typical pattern:
What this gives you:
A single emitter that both produces and streams events.
A clean async generator interface for consumers.
A clear error if your handler configuration is incompatible with streaming.
Lifecycle and Cleanup
Proper cleanup is important, especially when handlers maintain resources.
close()should be called when you are done emitting events.It asynchronously iterates over all handlers and calls
await handler.close().This allows handlers to release resources (e.g., flush buffers, close streams, stop background tasks).
A typical application lifecycle might look like:
Construct an
EventEmitterat startup (possibly via a factory method).Pass it through pipelines/components that need to emit events.
Call
await emitter.close()at shutdown to clean up resources.
Best Practices
Use factory constructors in simple cases
Prefer
with_console_handler,with_print_handler, orwith_stream_handlerwhenever they fit your needs.Fall back to the raw constructor only for advanced multi-handler setups.
Set an appropriate severity threshold
Start with
EventLevel.INFOorEventLevel.WARNINGin production.Switch to
EventLevel.DEBUGtemporarily when diagnosing issues.
Keep hooks side-effect free
Treat hooks as pure transformations from
Event→Event.Avoid blocking I/O or heavy computation inside hooks.
Name your handlers meaningfully
Set handler names so
disabled_handlerscan selectively skip them when needed.Use this to, for example, suppress streaming while still logging to the console.
Integrate with Components and Pipelines
Pass a shared
EventEmitterinto components (e.g., viaevent_emitterkwargs) so they can emit start/finish messages.Use hooks to normalize event structure across heterogeneous components.
Last updated