@mozaik-ai/core 3.1.1 → 3.1.3
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 +252 -65
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
# Mozaik
|
|
2
2
|
|
|
3
|
-
**Mozaik** is a TypeScript framework for building
|
|
3
|
+
**Mozaik** is a TypeScript framework for building non-blocking AI agents.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
With Mozaik, you can:
|
|
8
|
-
|
|
9
|
-
- Structure interactions as ordered **context items** (messages, reasoning steps, function calls, etc.)
|
|
10
|
-
- Append and evolve context across multiple model calls
|
|
11
|
-
- Persist and reload context from storage
|
|
12
|
-
- Manage context size and avoid overflow
|
|
13
|
-
- Build complex workflows through **context composition**, not ad-hoc prompt strings
|
|
14
|
-
|
|
15
|
-
Mozaik treats context as a **first-class primitive**, making it easier to design scalable, maintainable, and provider-agnostic LLM applications.
|
|
16
|
-
|
|
17
|
-

|
|
5
|
+
It provides a structured agent loop, a lifecycle hook system, a typed context model, and an experimental multi-agent runtime — giving you full control over how agents think, act, and collaborate.
|
|
18
6
|
|
|
19
7
|
---
|
|
20
8
|
|
|
21
|
-
##
|
|
9
|
+
## Installation
|
|
22
10
|
|
|
23
11
|
```bash
|
|
24
12
|
yarn add @mozaik-ai/core
|
|
@@ -26,94 +14,293 @@ yarn add @mozaik-ai/core
|
|
|
26
14
|
|
|
27
15
|
## API Key Configuration
|
|
28
16
|
|
|
29
|
-
Make sure to set your API keys in a `.env` file at the root of your project:
|
|
30
|
-
|
|
31
17
|
```env
|
|
32
|
-
#
|
|
18
|
+
# .env
|
|
33
19
|
OPENAI_API_KEY=your-openai-key-here
|
|
34
20
|
```
|
|
35
21
|
|
|
36
|
-
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Architecture
|
|
25
|
+
|
|
26
|
+
Mozaik is organized into four layers:
|
|
27
|
+
|
|
28
|
+
```mermaid
|
|
29
|
+
graph TD
|
|
30
|
+
AgentSociety["AgentSociety (experimental)"] --> Agent
|
|
31
|
+
Agent --> AgentRuntime
|
|
32
|
+
AgentRuntime --> AgentLoop["AgentLoop (FSM + hooks)"]
|
|
33
|
+
AgentLoop --> OpenAIResponses["OpenAIResponses (OpenResponses adapter)"]
|
|
34
|
+
AgentLoop --> Context["Context + ContextItems"]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| Layer | Responsibility |
|
|
38
|
+
| ----------------------- | ------------------------------------------------------ |
|
|
39
|
+
| **AgentSociety** | Runs multiple agents concurrently (experimental) |
|
|
40
|
+
| **Agent** | Lifecycle callbacks, visitor hooks, high-level `run()` |
|
|
41
|
+
| **AgentRuntime** | Drives the loop, dispatches hooks, manages errors |
|
|
42
|
+
| **AgentLoop + Context** | FSM transitions and typed context model |
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Core Concepts
|
|
47
|
+
|
|
48
|
+
### Agent Loop
|
|
49
|
+
|
|
50
|
+
The agent loop is a **finite state machine** that drives a multi-step inference cycle. Each execution moves through states until it reaches a terminal condition.
|
|
51
|
+
|
|
52
|
+
```mermaid
|
|
53
|
+
stateDiagram-v2
|
|
54
|
+
[*] --> INFERENCE_PENDING
|
|
55
|
+
INFERENCE_PENDING --> FUNCTION_CALL_PENDING: model returned a function call
|
|
56
|
+
INFERENCE_PENDING --> MODEL_MESSAGE_RECEIVED: model returned a message
|
|
57
|
+
FUNCTION_CALL_PENDING --> INFERENCE_PENDING: tool executed, feeding output back
|
|
58
|
+
MODEL_MESSAGE_RECEIVED --> [*]: execution complete
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
An `Execution` object tracks the current state, step count, transition history, and terminal status (`RUNNING`, `COMPLETED`, `FAILED`).
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
### Hooks (Events)
|
|
66
|
+
|
|
67
|
+
Every state transition exposes **before** and **after** hooks. You register callbacks on an `AgentRuntime` using `.on(hookId, callback)`:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { AgentRuntime, HookId } from "@mozaik-ai/core"
|
|
71
|
+
|
|
72
|
+
const runtime = new AgentRuntime()
|
|
73
|
+
|
|
74
|
+
runtime.on(HookId.BEFORE_INFERENCE, async (context) => {
|
|
75
|
+
console.log("About to call the model...")
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
runtime.on(HookId.AFTER_INFERENCE, async (context) => {
|
|
79
|
+
console.log("Model responded:", context.inferenceResponse)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
runtime.on(HookId.AFTER_FUNCTION_CALL, async (context) => {
|
|
83
|
+
console.log("Tool executed:", context.functionCallOutput)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
runtime.on(HookId.ON_ERROR, async (context) => {
|
|
87
|
+
console.error("Agent failed:", context.error)
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Available hooks:
|
|
92
|
+
|
|
93
|
+
| Hook | Fires |
|
|
94
|
+
| ---------------------- | ------------------------------------ |
|
|
95
|
+
| `BEFORE_INFERENCE` | Before the model is called |
|
|
96
|
+
| `AFTER_INFERENCE` | After the model responds |
|
|
97
|
+
| `BEFORE_FUNCTION_CALL` | Before a tool is invoked |
|
|
98
|
+
| `AFTER_FUNCTION_CALL` | After a tool returns |
|
|
99
|
+
| `BEFORE_MODEL_MESSAGE` | Before the final message is received |
|
|
100
|
+
| `AFTER_MODEL_MESSAGE` | After the final message is received |
|
|
101
|
+
| `ON_ERROR` | When any state handler throws |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Agent
|
|
106
|
+
|
|
107
|
+
`Agent` wraps an `AgentRuntime` and pre-wires all hooks as overridable methods. Subclass it to customize behavior at any point in the lifecycle:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { Agent, AgentRuntime, RuntimeContext } from "@mozaik-ai/core"
|
|
111
|
+
|
|
112
|
+
class MyAgent extends Agent {
|
|
113
|
+
constructor() {
|
|
114
|
+
super(new AgentRuntime())
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async afterInference(context: RuntimeContext): Promise<void> {
|
|
118
|
+
console.log("Tokens used:", context.inferenceResponse?.tokenUsage)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async onError(context: RuntimeContext): Promise<void> {
|
|
122
|
+
console.error("Something went wrong:", context.error?.message)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const agent = new MyAgent()
|
|
127
|
+
await agent.run("Summarize the latest news", model, context)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
37
131
|
|
|
38
|
-
|
|
132
|
+
### InferenceVisitor
|
|
39
133
|
|
|
40
|
-
|
|
134
|
+
`InferenceVisitor` is a **visitor interface** that lets you observe the inference lifecycle without modifying the agent itself. Attach it to any `Agent` instance via `setInferenceVisitor`.
|
|
41
135
|
|
|
42
|
-
|
|
136
|
+
```ts
|
|
137
|
+
import { InferenceVisitor, InferenceResponse, RuntimeContext } from "@mozaik-ai/core"
|
|
138
|
+
|
|
139
|
+
class TelemetryVisitor implements InferenceVisitor {
|
|
140
|
+
async onStart(context: RuntimeContext): Promise<void> {
|
|
141
|
+
console.log("Agent started, execution:", context.execution.executionId)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async afterInference(response: InferenceResponse): Promise<void> {
|
|
145
|
+
console.log("Inference complete, tokens:", response.tokenUsage)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const agent = new MyAgent()
|
|
150
|
+
agent.setInferenceVisitor(new TelemetryVisitor())
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Use `InferenceVisitor` for concerns that should stay separate from agent logic: logging, telemetry, streaming responses to a UI, or recording usage metrics.
|
|
43
154
|
|
|
44
155
|
---
|
|
45
156
|
|
|
46
|
-
|
|
157
|
+
### Context Model
|
|
47
158
|
|
|
48
|
-
|
|
159
|
+
`Context` is the structured input passed to the model. It is composed of ordered **`ContextItem`** objects — each representing a single unit of context.
|
|
49
160
|
|
|
50
|
-
|
|
161
|
+
**Client-provided items** (you add these):
|
|
51
162
|
|
|
52
|
-
|
|
163
|
+
- `UserMessage` — the user's turn
|
|
164
|
+
- `DeveloperMessage` — system instructions
|
|
165
|
+
- `FunctionCallOutput` — tool result fed back to the model
|
|
53
166
|
|
|
54
|
-
- **
|
|
55
|
-
- **Output (response)**: model-produced context items such as **reasoning**, **function_call**, and **model_message**
|
|
56
|
-
- **Streaming (optional)**: items may be delivered incrementally as **semantic events** (meaningful events at the item level, not just raw token streams)
|
|
167
|
+
**Model-produced items** (appended by the loop):
|
|
57
168
|
|
|
58
|
-
|
|
169
|
+
- `ModelMessage` — the model's final text response
|
|
170
|
+
- `FunctionCall` — a tool invocation requested by the model
|
|
171
|
+
- `Reasoning` — the model's internal reasoning trace
|
|
59
172
|
|
|
60
|
-
|
|
173
|
+
```ts
|
|
174
|
+
import { Context, DeveloperMessage, UserMessage, InMemoryContextRepository } from "@mozaik-ai/core"
|
|
175
|
+
|
|
176
|
+
const context = Context.create("project-id")
|
|
177
|
+
.addItem(DeveloperMessage.create("You are a helpful assistant."))
|
|
178
|
+
.addItem(UserMessage.create("What is the capital of France?"))
|
|
61
179
|
|
|
62
|
-
|
|
180
|
+
const repo = new InMemoryContextRepository()
|
|
181
|
+
await repo.save(context)
|
|
63
182
|
|
|
64
|
-
|
|
183
|
+
// later...
|
|
184
|
+
const restored = await repo.getByProjectId("project-id")
|
|
185
|
+
```
|
|
65
186
|
|
|
66
|
-
|
|
187
|
+
Context can be persisted and restored via `ContextRepository`. The built-in `InMemoryContextRepository` is suitable for development; implement `ContextRepository` to connect any storage backend.
|
|
67
188
|
|
|
68
189
|
---
|
|
69
190
|
|
|
70
|
-
###
|
|
191
|
+
### OpenResponses
|
|
192
|
+
|
|
193
|
+
`OpenAIResponses` is the framework's inference provider. It implements the **OpenResponses** specification ([openresponses.org](https://www.openresponses.org/)), mapping Mozaik's typed `Context` to the OpenAI Responses API and back.
|
|
71
194
|
|
|
72
|
-
|
|
195
|
+
It is the default provider used by `AgentRuntime` and handles:
|
|
73
196
|
|
|
74
|
-
|
|
197
|
+
- Serializing `ContextItem` objects to API-compatible input
|
|
198
|
+
- Deserializing model output into `ModelMessage`, `FunctionCall`, and `Reasoning` items
|
|
199
|
+
- Extracting token usage from the response
|
|
75
200
|
|
|
76
|
-
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
201
|
+
You can use it directly for single-shot inference outside the agent loop:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
import { OpenAIResponses, InferenceRequest, Gpt54 } from "@mozaik-ai/core"
|
|
205
|
+
|
|
206
|
+
const openaiResponses = new OpenAIResponses()
|
|
207
|
+
const request = new InferenceRequest(new Gpt54(), context)
|
|
208
|
+
const response = await openaiResponses.infer(request)
|
|
209
|
+
|
|
210
|
+
context.applyModelOutput(response.contextItems)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Available models: `Gpt54`, `Gpt54Mini`, `Gpt54Nano`.
|
|
84
214
|
|
|
85
215
|
---
|
|
86
216
|
|
|
87
|
-
|
|
217
|
+
### AgentSociety _(experimental)_
|
|
88
218
|
|
|
89
|
-
|
|
219
|
+
`AgentSociety` is the framework's answer to one of the core limitations in today's agent systems: **agents that block on each other**. Most multi-agent frameworks run agents sequentially, which creates bottlenecks and prevents true parallelism.
|
|
90
220
|
|
|
91
|
-
-
|
|
92
|
-
- calls a model (OpenAI Responses API)
|
|
93
|
-
- stores/restores the context using a repository
|
|
221
|
+
`AgentSociety` runs agents **concurrently and non-blocking**. When `enter()` is called, every agent in the society starts its execution independently — none waits for another to finish.
|
|
94
222
|
|
|
95
223
|
```ts
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
224
|
+
import { AgentSociety, AgentRuntime } from "@mozaik-ai/core"
|
|
225
|
+
|
|
226
|
+
const society = new AgentSociety("research-team")
|
|
227
|
+
|
|
228
|
+
society.join(new ResearchAgent())
|
|
229
|
+
society.join(new SummaryAgent())
|
|
230
|
+
society.join(new FactCheckAgent())
|
|
231
|
+
|
|
232
|
+
// Start the society's event loop
|
|
233
|
+
society.start()
|
|
100
234
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const context = Context.create(projectId).addItem(developerMessage).addItem(message)
|
|
235
|
+
// All three agents run in parallel on the same context
|
|
236
|
+
society.enter("Analyze the impact of AI on software development", model, context)
|
|
104
237
|
|
|
105
|
-
|
|
238
|
+
// Shut down when done
|
|
239
|
+
society.stop()
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
All agents receive the same `Context`, enabling **collaborative context building** — each agent's output is visible to others through the shared context as the execution progresses.
|
|
243
|
+
|
|
244
|
+
> **Experimental:** `AgentSociety` is in active development. The API is stable enough to build on, but expect refinements — particularly around context isolation, coordination primitives, and agent-to-agent messaging. Feedback and contributions are welcome.
|
|
245
|
+
|
|
246
|
+
---
|
|
106
247
|
|
|
107
|
-
|
|
108
|
-
const newContextItems = await openresponses.infer(gpt54, context)
|
|
109
|
-
context.applyModelOutput(newContextItems)
|
|
248
|
+
## Full Example
|
|
110
249
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
250
|
+
End-to-end example with a tool-calling agent and hook observability:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
import {
|
|
254
|
+
Agent,
|
|
255
|
+
AgentRuntime,
|
|
256
|
+
HookId,
|
|
257
|
+
Context,
|
|
258
|
+
DeveloperMessage,
|
|
259
|
+
Gpt54Mini,
|
|
260
|
+
InMemoryContextRepository,
|
|
261
|
+
Tool,
|
|
262
|
+
} from "@mozaik-ai/core"
|
|
263
|
+
|
|
264
|
+
// Define a tool
|
|
265
|
+
const weatherTool = new Tool({
|
|
266
|
+
name: "get_weather",
|
|
267
|
+
description: "Get the current weather for a city",
|
|
268
|
+
parameters: {
|
|
269
|
+
type: "object",
|
|
270
|
+
properties: { city: { type: "string" } },
|
|
271
|
+
required: ["city"],
|
|
272
|
+
},
|
|
273
|
+
invoke: async ({ city }) => ({ temperature: "22°C", condition: "sunny" }),
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
// Create runtime and wire hooks
|
|
277
|
+
const runtime = new AgentRuntime()
|
|
278
|
+
|
|
279
|
+
runtime.on(HookId.BEFORE_INFERENCE, async (ctx) => {
|
|
280
|
+
console.log(`[step ${ctx.execution.stepCount}] calling model...`)
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
runtime.on(HookId.AFTER_FUNCTION_CALL, async (ctx) => {
|
|
284
|
+
console.log("tool result:", ctx.functionCallOutput)
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
// Create agent
|
|
288
|
+
const agent = new Agent(runtime)
|
|
289
|
+
|
|
290
|
+
// Set up model and context
|
|
291
|
+
const model = new Gpt54Mini([weatherTool])
|
|
292
|
+
const context = Context.create("demo").addItem(DeveloperMessage.create("You are a weather assistant."))
|
|
293
|
+
|
|
294
|
+
const repo = new InMemoryContextRepository()
|
|
295
|
+
await repo.save(context)
|
|
296
|
+
|
|
297
|
+
// Run
|
|
298
|
+
await agent.run("What's the weather like in Tokyo?", model, context)
|
|
114
299
|
```
|
|
115
300
|
|
|
301
|
+
---
|
|
302
|
+
|
|
116
303
|
## Author & License
|
|
117
304
|
|
|
118
|
-
Created by [JigJoy](https://jigjoy.io) team
|
|
305
|
+
Created by the [JigJoy](https://jigjoy.io) team.
|
|
119
306
|
Licensed under the MIT License.
|