@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.
- package/README.md +66 -99
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Mozaik
|
|
2
2
|
|
|
3
|
-
**Mozaik** is
|
|
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
|
  
|
|
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
|
|
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
|
-
|
|
|
31
|
-
|
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
##
|
|
232
|
+
## Overriding Generators
|
|
239
233
|
|
|
240
|
-
|
|
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
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
249
|
+
Each yielded string is delivered to other participants through `onMessage(message: string)`.
|
|
279
250
|
|
|
280
251
|
### Custom `InferenceRunner`
|
|
281
252
|
|
|
282
|
-
|
|
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
|
-
|
|
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
|
|
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
|