Location within the framework
bee-agent-framework/emitter
.
An emitter is a core functionality of the framework that allows you to see what is happening under the hood.
The following examples demonstrate how Emitter
concept works.
import { Emitter, EventMeta } from "bee-agent-framework/emitter/emitter";
// Get the root emitter or create your own
const root = Emitter.root;
root.match("*.*", async (data: unknown, event: EventMeta) => {
console.log(`Received event '${event.path}' with data ${JSON.stringify(data)}`);
});
await root.emit("start", { id: 123 });
await root.emit("end", { id: 123 });
Source: examples/emitter/base.ts
Note
You can create your own emitter by initiating the Emitter
class, but typically it's better to use or fork the root one (as can be seen in the following examples).
import { Emitter, EventMeta, Callback } from "bee-agent-framework/emitter/emitter";
// Define events in advanced
interface Events {
start: Callback<{ id: number }>;
update: Callback<{ id: number; data: string }>;
}
// Create emitter with a type support
const emitter = Emitter.root.child<Events>({
namespace: ["bee", "demo"],
creator: {}, // typically a class
context: {}, // custom data (propagates to the event's context property)
groupId: undefined, // optional id for grouping common events (propagates to the event's groupId property)
trace: undefined, // data related to identity what emitted what and which context (internally used by framework's components)
});
// Listen for "start" event
emitter.on("start", async (data, event: EventMeta) => {
console.log(`Received ${event.name} event with id "${data.id}"`);
});
// Listen for "update" event
emitter.on("update", async (data, event: EventMeta) => {
console.log(`Received ${event.name}' with id "${data.id}" and data ${data.data}`);
});
await emitter.emit("start", { id: 123 });
await emitter.emit("update", { id: 123, data: "Hello Bee!" });
Source: examples/emitter/advanced.ts
Note
Because we created the Emitter
instance directly emitted events will not be propagated to the root
which may or may not be desired.
The piping
concept is explained later on.
import { Callback, Emitter } from "bee-agent-framework/emitter/emitter";
import { BaseLLM } from "bee-agent-framework/llms/base";
interface Events {
update: Callback<{ data: string }>;
}
const emitter = new Emitter<Events>({
namespace: ["app"],
});
// Match events by a concrete name (strictly typed)
emitter.on("update", async (data, event) => {});
// Match all events emitted directly on the instance (not nested)
emitter.match("*", async (data, event) => {});
// Match all events (included nested)
emitter.match("*.*", async (data, event) => {});
// Match events by providing a filter function
emitter.match(
(event) => event.creator instanceof BaseLLM,
async (data, event) => {},
);
// Match events by regex
emitter.match(/watsonx/, async (data, event) => {});
Source: examples/emitter/matchers.ts
import { Emitter, EventMeta } from "bee-agent-framework/emitter/emitter";
const first = new Emitter({
namespace: ["app"],
});
first.match("*.*", (data: unknown, event: EventMeta) => {
console.log(
`'first' has retrieved the following event ${event.path}, isDirect: ${event.source === first}`,
);
});
const second = new Emitter({
namespace: ["app", "llm"],
});
second.match("*.*", (data: unknown, event: EventMeta) => {
console.log(
`'second' has retrieved the following event '${event.path}', isDirect: ${event.source === second}`,
);
});
// Propagate all events from the 'second' emitter to the 'first' emitter
const unpipe = second.pipe(first);
await first.emit("a", {});
await second.emit("b", {});
console.log("Unpipe");
unpipe();
await first.emit("c", {});
await second.emit("d", {});
Source: examples/emitter/piping.ts
Typically, you consume out-of-the-box modules that use the Emitter
concept on your behalf.
import { BeeAgent } from "bee-agent-framework/agents/bee/agent";
import { UnconstrainedMemory } from "bee-agent-framework/memory/unconstrainedMemory";
import { OllamaChatLLM } from "bee-agent-framework/adapters/ollama/chat";
const agent = new BeeAgent({
llm: new OllamaChatLLM(),
memory: new UnconstrainedMemory(),
tools: [],
});
// Matching events on the instance level
agent.emitter.match("*.*", (data, event) => {});
await agent
.run({
prompt: "Hello agent!",
})
.observe((emitter) => {
// Matching events on the execution (run) level
emitter.match("*.*", (data, event) => {
console.info(`RUN LOG: received event '${event.path}'`);
});
});
Source: examples/emitter/agentMatchers.ts
Tip
The more complex agentic example can be found here.
Tip
To verify if a given class instance has one, check for the presence of the emitter
property.