@mozaik-ai/core 3.8.3 → 3.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +66 -99
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Mozaik
2
2
 
3
- **Mozaik** is an open-source TypeScript framework for building reactive AI agents that share an **agentic environment** instead of being orchestrated through rigid workflows.
3
+ **Mozaik** is TypeScript framework for building reactive agents. It provides the easiest way to build collaborative, **event-driven agents** that can work together in parallel.
4
4
 
5
5
  ![npm downloads](https://img.shields.io/npm/dt/@mozaik-ai/core) ![npm downloads weekly](https://img.shields.io/npm/dw/@mozaik-ai/core) ![npm version](https://img.shields.io/npm/v/@mozaik-ai/core)
6
6
 
@@ -25,17 +25,21 @@ OPENAI_API_KEY=your-openai-key-here
25
25
 
26
26
  ## The agentic environment
27
27
 
28
- `AgenticEnvironment` is a broadcast bus for typed events. `Participant`s `join()` it, and anything produced by one participant is delivered to every subscriber through a **dedicated typed callback** — one per event kind, with separate handlers for items the participant emitted itself versus items emitted by someone else:
28
+ `AgenticEnvironment` is where everything happens. `Participant`s `join()` it, and from that moment on they can **listen to messages and events** flowing through the environment by overriding any of the handlers below:
29
29
 
30
- | Producer call | Self handler | External handler |
31
- | ----------------------------------------- | ---------------------- | ----------------------------------------- |
32
- | `deliverFunctionCall(source, item)` | `onFunctionCall` | `onExternalFunctionCall(source, …)` |
33
- | `deliverFunctionCallOutput(source, item)` | `onFunctionCallOutput` | `onExternalFunctionCallOutput(source, …)` |
34
- | `deliverReasoning(source, item)` | `onReasoning` | `onExternalReasoning(source, …)` |
35
- | `deliverModelMessage(source, item)` | `onModelMessage` | `onExternalModelMessage(source, …)` |
36
- | `deliverMessage(source, message)` | _(not delivered)_ | `onMessage(message)` |
30
+ | Handler | Triggered when… |
31
+ | ------------------------------ | ------------------------------------------------------ |
32
+ | `onMessage` | any participant sends a message |
33
+ | `onFunctionCall` | its own inference returns a function call |
34
+ | `onExternalFunctionCall` | another agent's inference returns a function call |
35
+ | `onFunctionCallOutput` | its own function call runner returns a result |
36
+ | `onExternalFunctionCallOutput` | another agent's function call runner returns a result |
37
+ | `onReasoning` | its own inference returns a reasoning item |
38
+ | `onExternalReasoning` | another agent's inference returns a reasoning item |
39
+ | `onModelMessage` | its own inference returns an assistant message |
40
+ | `onExternalModelMessage` | another agent's inference returns an assistant message |
37
41
 
38
- Messages are plain `string`s exchanged between participants. Every other event flows as a typed `ContextItem` (`FunctionCallItem`, `FunctionCallOutputItem`, `ReasoningItem`, `ModelMessageItem`).
42
+ Every handler defaults to a no-op — override only the ones you care about.
39
43
 
40
44
  ```mermaid
41
45
  flowchart LR
@@ -89,58 +93,7 @@ The environment fans every item out to every subscriber synchronously and withou
89
93
 
90
94
  ---
91
95
 
92
- ## Intercepting events from other participants
93
-
94
- Instead of a single catch-all callback, `Participant` exposes **one typed handler per event kind**, with a clear split between events the participant emitted itself and events emitted by someone else. A participant can:
95
-
96
- - **Observe** events from other participants via the `onExternal*` family (telemetry, audit, UI streaming) and `onMessage` for plain-text messages.
97
- - **React** to its own outputs via the self handlers (e.g. execute a `FunctionCallItem` it just produced), or to others' outputs via the external handlers.
98
- - **Ignore** events it doesn't care about — every handler defaults to a no-op in the bundled participants.
99
-
100
- The typed `ContextItem`s involved are defined in [src/domain/model-context/context-item](src/domain/model-context/context-item):
101
-
102
- - Client-produced: `FunctionCallOutputItem` (and `UserMessageItem` / `DeveloperMessageItem` / `SystemMessageItem` when building a `ModelContext` directly)
103
- - Model-produced: `ModelMessageItem`, `FunctionCallItem`, `ReasoningItem`
104
-
105
- Plain conversational input flows as a `string` through `onMessage` — participants exchange messages without committing to a specific `ContextItem` shape on the wire.
106
-
107
- ### Passive observer
108
-
109
- A passive observer subclasses `Participant` and overrides only the handlers it cares about:
110
-
111
- ```ts
112
- import { Participant, FunctionCallItem, FunctionCallOutputItem, ReasoningItem, ModelMessageItem } from "@mozaik-ai/core"
113
-
114
- export class TranscriptLogger extends Participant {
115
- async onMessage(message: string): Promise<void> {
116
- console.log("[message]", message)
117
- }
118
-
119
- async onExternalFunctionCall(source: Participant, item: FunctionCallItem): Promise<void> {
120
- console.log(`[${source.constructor.name}] function_call`, item.toJSON())
121
- }
122
-
123
- async onExternalFunctionCallOutput(source: Participant, item: FunctionCallOutputItem): Promise<void> {
124
- console.log(`[${source.constructor.name}] function_call_output`, item.toJSON())
125
- }
126
-
127
- async onExternalReasoning(source: Participant, item: ReasoningItem): Promise<void> {
128
- console.log(`[${source.constructor.name}] reasoning`, item.toJSON())
129
- }
130
-
131
- async onExternalModelMessage(source: Participant, item: ModelMessageItem): Promise<void> {
132
- console.log(`[${source.constructor.name}] model_message`, item.toJSON())
133
- }
134
-
135
- // Self-emitted handlers (onFunctionCall, onReasoning, …) can be no-ops for a pure observer.
136
- async onFunctionCall(): Promise<void> {}
137
- async onFunctionCallOutput(): Promise<void> {}
138
- async onReasoning(): Promise<void> {}
139
- async onModelMessage(): Promise<void> {}
140
- }
141
- ```
142
-
143
- ### Reactive agent
96
+ ## Reactive agent
144
97
 
145
98
  A reactive agent extends `BaseAgentParticipant` and overrides the handlers it wants to react on. Each handler is already a no-op in the base class, so only the relevant ones need bodies:
146
99
 
@@ -207,6 +160,47 @@ Three things to note:
207
160
 
208
161
  ---
209
162
 
163
+ ## Reacting to external events
164
+
165
+ Participants can listen to external events and react by overriding methods like `onMessage`, `onExternalFunctionCall`, `onExternalFunctionCallOutput`, `onExternalReasoning`, and `onExternalModelMessage`.
166
+
167
+ ## Passive observer
168
+
169
+ You can create observers that don't run inference themselves but watch what's happening in the conversation and take side actions (logging, metrics, persistence, etc.). Subclass `Participant` and override only the handlers you care about:
170
+
171
+ ```ts
172
+ import { Participant, FunctionCallItem, FunctionCallOutputItem, ReasoningItem, ModelMessageItem } from "@mozaik-ai/core"
173
+
174
+ export class TranscriptLogger extends Participant {
175
+ async onMessage(message: string): Promise<void> {
176
+ console.log("[message]", message)
177
+ }
178
+
179
+ async onExternalFunctionCall(source: Participant, item: FunctionCallItem): Promise<void> {
180
+ console.log(`[${source.constructor.name}] function_call`, item.toJSON())
181
+ }
182
+
183
+ async onExternalFunctionCallOutput(source: Participant, item: FunctionCallOutputItem): Promise<void> {
184
+ console.log(`[${source.constructor.name}] function_call_output`, item.toJSON())
185
+ }
186
+
187
+ async onExternalReasoning(source: Participant, item: ReasoningItem): Promise<void> {
188
+ console.log(`[${source.constructor.name}] reasoning`, item.toJSON())
189
+ }
190
+
191
+ async onExternalModelMessage(source: Participant, item: ModelMessageItem): Promise<void> {
192
+ console.log(`[${source.constructor.name}] model_message`, item.toJSON())
193
+ }
194
+
195
+ // Self-emitted handlers (onFunctionCall, onReasoning, …) can be no-ops for a pure observer.
196
+ async onFunctionCall(): Promise<void> {}
197
+ async onFunctionCallOutput(): Promise<void> {}
198
+ async onReasoning(): Promise<void> {}
199
+ async onModelMessage(): Promise<void> {}
200
+ }
201
+ ```
202
+ ---
203
+
210
204
  ## Context and models (reference)
211
205
 
212
206
  `ModelContext` is the ordered list of `ContextItem`s a `GenerativeModel` is asked to reason over. It is constructed and mutated explicitly — typically inside a participant in response to delivered items.
@@ -235,51 +229,28 @@ const response = await runtime.infer(new InferenceRequest(new Gpt54(), context))
235
229
 
236
230
  ---
237
231
 
238
- ## Advanced: overriding generators
232
+ ## Overriding Generators
239
233
 
240
- `BaseAgentParticipant` and `BaseHumanParticipant` are deliberately thin shells around three generator interfaces. Swap any of them to change _how_ events are produced without touching the environment, the participants, or any consumers.
234
+ Mozaik uses generators for inference, function calls, and message streams — that's what lets the system emit events incrementally so participants can react to them as they arrive. Swap any generator to change _how_ events are produced.
241
235
 
242
236
  ### Custom `InputStream`
243
237
 
244
- An `InputStream` is the minimal text-source contract a participant uses to feed messages into the environment:
245
-
246
- ```ts
247
- export interface InputStream {
248
- stream(signal?: AbortSignal): AsyncIterable<string>
249
- }
250
- ```
251
-
252
- Each yielded string is delivered to every other participant through `onMessage(message: string)`. This keeps the wire format dead simple: participants exchange text, and each one decides how to turn that text into a `ContextItem` for its own `ModelContext`.
253
-
254
238
  ```ts
255
239
  import { InputStream } from "@mozaik-ai/core"
256
240
 
257
- export class QueueInputStream implements InputStream {
258
- private readonly queue: string[] = []
259
- private resolveNext?: () => void
260
-
261
- push(message: string) {
262
- this.queue.push(message)
263
- this.resolveNext?.()
264
- this.resolveNext = undefined
265
- }
266
-
267
- async *stream(signal?: AbortSignal): AsyncIterable<string> {
268
- while (!signal?.aborted) {
269
- while (this.queue.length > 0) {
270
- yield this.queue.shift()!
271
- }
272
- await new Promise<void>((resolve) => (this.resolveNext = resolve))
273
- }
241
+ export class HelloInputStream implements InputStream {
242
+ async *stream(): AsyncIterable<string> {
243
+ yield "Hello"
244
+ yield "World"
274
245
  }
275
246
  }
276
247
  ```
277
248
 
278
- Use it for stdin, websockets, an HTTP queue, or anything that produces text over time. A reactive agent typically wraps the incoming string in a `UserMessageItem` inside `onMessage` before adding it to its `ModelContext` — see the [Reactive agent](#reactive-agent) example above.
249
+ Each yielded string is delivered to other participants through `onMessage(message: string)`.
279
250
 
280
251
  ### Custom `InferenceRunner`
281
252
 
282
- Wrap any model runtime — including `OpenAIResponses` — and decide how its output becomes a stream of items. Here we expand a single `InferenceResponse` into per-item delivery:
253
+ An `InferenceRunner` can yield `ReasoningItem`, `FunctionCallItem`, and `ModelMessageItem`.
283
254
 
284
255
  ```ts
285
256
  import {
@@ -307,11 +278,9 @@ export class OpenAIInferenceRunner implements InferenceRunner {
307
278
  }
308
279
  ```
309
280
 
310
- Replace the body with a streaming runtime and items will flow into the environment as soon as the model produces them.
311
-
312
281
  ### Custom `FunctionCallRunner`
313
282
 
314
- Resolve a `FunctionCallItem` against a tool registry and yield its output:
283
+ A `FunctionCallRunner` can only produce `FunctionCallOutputItem`.
315
284
 
316
285
  ```ts
317
286
  import { FunctionCallRunner, FunctionCallItem, FunctionCallOutputItem, Tool } from "@mozaik-ai/core"
@@ -335,7 +304,7 @@ export class ToolRegistryFunctionCallRunner implements FunctionCallRunner {
335
304
  import { BaseAgentParticipant, AgenticEnvironment } from "@mozaik-ai/core"
336
305
 
337
306
  const agent = new BaseAgentParticipant(
338
- new QueueInputStream(),
307
+ new HelloInputStream(),
339
308
  new OpenAIInferenceRunner(),
340
309
  new ToolRegistryFunctionCallRunner(tools),
341
310
  )
@@ -343,8 +312,6 @@ const agent = new BaseAgentParticipant(
343
312
  agent.join(new AgenticEnvironment())
344
313
  ```
345
314
 
346
- You now own input, inference, and tool execution end-to-end while keeping the same `Participant` contract — and any other participant in the environment can still observe and react to everything the agent emits.
347
-
348
315
  ---
349
316
 
350
317
  ## Made with Mozaik
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mozaik-ai/core",
3
- "version": "3.8.3",
3
+ "version": "3.8.5",
4
4
  "description": "A TypeScript framework for building reactive agents",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",