@mozaik-ai/core 3.12.0 → 3.13.1
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 +209 -102
- package/dist/index.d.mts +108 -570
- package/dist/index.d.ts +108 -570
- package/dist/index.js +1102 -1101
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1096 -1075
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
  
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Mozaik is built for **truly parallel, non-blocking agents**. It enables agents to communicate through semantic events, react to each other’s outputs, run asynchronously, and collaborate without being locked into a single sequential workflow.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -30,11 +30,17 @@ pnpm add @mozaik-ai/core
|
|
|
30
30
|
|
|
31
31
|
## API Key Configuration
|
|
32
32
|
|
|
33
|
+
Mozaik picks a provider from the model name you pass to `runInference`, and each provider's SDK reads its credential from the environment. Set the keys for the providers you use:
|
|
34
|
+
|
|
33
35
|
```env
|
|
34
36
|
# .env
|
|
35
37
|
OPENAI_API_KEY=your-openai-key-here
|
|
38
|
+
ANTHROPIC_API_KEY=your-anthropic-key-here
|
|
39
|
+
GEMINI_API_KEY=your-gemini-key-here
|
|
36
40
|
```
|
|
37
41
|
|
|
42
|
+
DeepSeek models run through the OpenAI-compatible chat-completions endpoint, so they use an OpenAI-style credential and base URL (`OPENAI_API_KEY` / `OPENAI_BASE_URL`) pointed at DeepSeek.
|
|
43
|
+
|
|
38
44
|
---
|
|
39
45
|
|
|
40
46
|
## The agentic environment
|
|
@@ -58,14 +64,16 @@ OPENAI_API_KEY=your-openai-key-here
|
|
|
58
64
|
| `onExternalModelMessage` | another agent's inference returns an assistant message |
|
|
59
65
|
| `onInternalEvent` | its own inference emits a semantic stream event |
|
|
60
66
|
| `onExternalEvent` | another participant emits a semantic stream event |
|
|
67
|
+
| `onError` | one of its own handlers throws |
|
|
68
|
+
| `onParticipantError` | another participant's handler throws |
|
|
61
69
|
|
|
62
|
-
Every handler defaults to a no-op — override only the ones you care about.
|
|
70
|
+
Every handler defaults to a no-op on `BaseParticipant` — override only the ones you care about.
|
|
63
71
|
|
|
64
72
|
```mermaid
|
|
65
73
|
flowchart LR
|
|
66
|
-
Human[
|
|
67
|
-
Agent[
|
|
68
|
-
Observer[
|
|
74
|
+
Human[Participant] -->|"sendMessage(env, text, caller)"| Env(("AgenticEnvironment"))
|
|
75
|
+
Agent[Participant] -->|"runInference / executeFunctionCall"| Env
|
|
76
|
+
Observer[Participant] -->|join| Env
|
|
69
77
|
Env -->|"onMessage / onExternal*"| Human
|
|
70
78
|
Env -->|"onFunctionCall / onReasoning / …"| Agent
|
|
71
79
|
Env -->|"onExternal*"| Observer
|
|
@@ -76,52 +84,58 @@ flowchart LR
|
|
|
76
84
|
|
|
77
85
|
## Non-blocking participants
|
|
78
86
|
|
|
79
|
-
|
|
87
|
+
A participant is any subclass of `Participant`. Use `BaseParticipant` as a base when you only want to override a few handlers — every handler it defines is a no-op. The _role_ (human, agent, observer) is just which capability functions a participant calls and which handlers it overrides:
|
|
80
88
|
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
|
89
|
+
| Role | How to build it |
|
|
90
|
+
| -------- | --------------------------------------------------------------------------- |
|
|
91
|
+
| Human | A participant that calls `sendMessage(environment, text, caller)` |
|
|
92
|
+
| Agent | A participant that calls `runInference(...)` and `executeFunctionCall(...)` |
|
|
93
|
+
| Observer | A participant that only overrides handlers and never runs inference |
|
|
86
94
|
|
|
87
95
|
```ts
|
|
88
96
|
import {
|
|
89
97
|
AgenticEnvironment,
|
|
90
|
-
|
|
91
|
-
BaseHuman,
|
|
92
|
-
OpenAIInferenceRunner,
|
|
93
|
-
DefaultFunctionCallRunner,
|
|
94
|
-
Gpt54Mini,
|
|
98
|
+
BaseParticipant,
|
|
95
99
|
ModelContext,
|
|
100
|
+
UserMessageItem,
|
|
101
|
+
runInference,
|
|
102
|
+
sendMessage,
|
|
96
103
|
} from "@mozaik-ai/core"
|
|
97
104
|
|
|
98
105
|
const environment = new AgenticEnvironment()
|
|
99
106
|
|
|
100
|
-
const human = new
|
|
101
|
-
|
|
102
|
-
|
|
107
|
+
const human = new BaseParticipant()
|
|
108
|
+
|
|
109
|
+
class Agent extends BaseParticipant {
|
|
110
|
+
private readonly context = ModelContext.create("demo")
|
|
111
|
+
|
|
112
|
+
async onMessage(message: string): Promise<void> {
|
|
113
|
+
this.context.addContextItem(UserMessageItem.create(message))
|
|
114
|
+
runInference({ model: "gpt-5.4", context: this.context, caller: this, environment })
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const agent = new Agent()
|
|
119
|
+
const observer = new BaseParticipant()
|
|
103
120
|
|
|
104
121
|
human.join(environment)
|
|
105
122
|
agent.join(environment)
|
|
106
123
|
observer.join(environment)
|
|
107
124
|
|
|
108
|
-
environment
|
|
109
|
-
|
|
110
|
-
human.sendMessage(environment, "Hello")
|
|
125
|
+
sendMessage(environment, "Hello", human)
|
|
111
126
|
```
|
|
112
127
|
|
|
113
|
-
The environment fans every item out to every subscriber synchronously and without awaiting them, so a slow listener never blocks producers or other listeners.
|
|
128
|
+
Participants react as soon as they `join()` agentic environment. The environment fans every item out to every subscriber synchronously and without awaiting them, so a slow listener never blocks producers or other listeners.
|
|
114
129
|
|
|
115
130
|
---
|
|
116
131
|
|
|
117
132
|
## Reactive agent
|
|
118
133
|
|
|
119
|
-
A reactive agent extends `
|
|
134
|
+
A reactive agent extends `BaseParticipant` 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. Capabilities are the free functions `runInference` and `executeFunctionCall` — the participant passes itself as `caller`:
|
|
120
135
|
|
|
121
136
|
```ts
|
|
122
137
|
import {
|
|
123
|
-
|
|
124
|
-
Participant,
|
|
138
|
+
BaseParticipant,
|
|
125
139
|
UserMessageItem,
|
|
126
140
|
FunctionCallItem,
|
|
127
141
|
FunctionCallOutputItem,
|
|
@@ -129,38 +143,50 @@ import {
|
|
|
129
143
|
ModelMessageItem,
|
|
130
144
|
AgenticEnvironment,
|
|
131
145
|
ModelContext,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
146
|
+
ModelName,
|
|
147
|
+
Tool,
|
|
148
|
+
runInference,
|
|
149
|
+
executeFunctionCall,
|
|
135
150
|
} from "@mozaik-ai/core"
|
|
136
151
|
|
|
137
|
-
export class ReactiveAgent extends
|
|
152
|
+
export class ReactiveAgent extends BaseParticipant {
|
|
138
153
|
constructor(
|
|
139
|
-
inferenceRunner: InferenceRunner,
|
|
140
|
-
functionCallRunner: FunctionCallRunner,
|
|
141
154
|
private readonly environment: AgenticEnvironment,
|
|
142
155
|
private readonly context: ModelContext,
|
|
143
|
-
private readonly
|
|
156
|
+
private readonly tools: Tool[] = [],
|
|
144
157
|
) {
|
|
145
|
-
super(
|
|
158
|
+
super()
|
|
146
159
|
}
|
|
147
160
|
|
|
148
161
|
// A message from a human (or any other participant) → record it and think.
|
|
149
162
|
async onMessage(message: string): Promise<void> {
|
|
150
163
|
this.context.addContextItem(UserMessageItem.create(message))
|
|
151
|
-
|
|
164
|
+
runInference({
|
|
165
|
+
model: 'gpt-5.5',
|
|
166
|
+
context: this.context,
|
|
167
|
+
tools: this.tools,
|
|
168
|
+
caller: this,
|
|
169
|
+
environment: this.environment,
|
|
170
|
+
})
|
|
152
171
|
}
|
|
153
172
|
|
|
154
173
|
// The agent just produced a function call → execute it.
|
|
155
174
|
async onFunctionCall(item: FunctionCallItem): Promise<void> {
|
|
156
175
|
this.context.addContextItem(item)
|
|
157
|
-
this.
|
|
176
|
+
const tool = this.tools.find((t) => t.name === item.name)
|
|
177
|
+
if (tool) executeFunctionCall(this.environment, item, tool, this)
|
|
158
178
|
}
|
|
159
179
|
|
|
160
180
|
// The tool just produced an output → feed it back and run inference again.
|
|
161
181
|
async onFunctionCallOutput(item: FunctionCallOutputItem): Promise<void> {
|
|
162
182
|
this.context.addContextItem(item)
|
|
163
|
-
|
|
183
|
+
runInference({
|
|
184
|
+
model: 'gpt-5.5',
|
|
185
|
+
context: this.context,
|
|
186
|
+
tools: this.tools,
|
|
187
|
+
caller: this,
|
|
188
|
+
environment: this.environment,
|
|
189
|
+
})
|
|
164
190
|
}
|
|
165
191
|
|
|
166
192
|
// Keep the local context in sync with model-emitted reasoning and replies.
|
|
@@ -177,22 +203,22 @@ export class ReactiveAgent extends BaseAgent {
|
|
|
177
203
|
Three things to note:
|
|
178
204
|
|
|
179
205
|
1. The split between self handlers and `onExternal*` handlers means a participant can encode "act on my own outputs" separately from "observe others", without inspecting `source` by hand.
|
|
180
|
-
2. The agent never `await`s its
|
|
206
|
+
2. The agent never `await`s its capability calls inside the handlers — `runInference` and `executeFunctionCall` are fire-and-forget (they return `void`), so the environment keeps delivering events while inference and tool execution run in the background.
|
|
181
207
|
3. Behaviors compose by **reaction**, not orchestration. Add a second agent that overrides `onExternalModelMessage` and you get a critique loop. Add a `TranscriptLogger` and you get a UI stream. Neither change touches the existing participants.
|
|
182
208
|
|
|
183
209
|
---
|
|
184
210
|
|
|
185
211
|
## Streaming and semantic events
|
|
186
212
|
|
|
187
|
-
When inference runs with streaming enabled (`
|
|
213
|
+
When inference runs with streaming enabled (`streaming: true` on the `runInference` params, for a model that supports it), the runner does not wait for the full response. As the provider emits chunks, the endpoint yields **`SemanticEvent`** items (`type` + `data`) and the environment delivers each one to **every** joined participant immediately — the same fan-out as messages and context items. Participants react in real time by overriding the stream handlers; no participant needs to poll or share a callback.
|
|
188
214
|
|
|
189
215
|
The producing participant receives `onInternalEvent`; everyone else receives `onExternalEvent(source, event)`:
|
|
190
216
|
|
|
191
217
|
```ts
|
|
192
|
-
import {
|
|
218
|
+
import { BaseParticipant, Participant, SemanticEvent } from "@mozaik-ai/core"
|
|
193
219
|
|
|
194
220
|
// Agent that runs streaming inference — can observe its own stream chunks.
|
|
195
|
-
export class StreamingAgent extends
|
|
221
|
+
export class StreamingAgent extends BaseParticipant {
|
|
196
222
|
async onInternalEvent(event: SemanticEvent<unknown>): Promise<void> {
|
|
197
223
|
if (event.type === "response.output_text.delta") {
|
|
198
224
|
// e.g. keep a local buffer of partial output
|
|
@@ -201,20 +227,71 @@ export class StreamingAgent extends BaseAgent {
|
|
|
201
227
|
}
|
|
202
228
|
|
|
203
229
|
// Any other participant — UI, logger, second agent — reacts to another's stream.
|
|
204
|
-
export class LiveTranscript extends
|
|
230
|
+
export class LiveTranscript extends BaseParticipant {
|
|
205
231
|
async onExternalEvent(source: Participant, event: SemanticEvent<unknown>): Promise<void> {
|
|
206
232
|
if (event.type === "response.output_text.delta") {
|
|
207
233
|
const { delta } = event.data as { delta: string }
|
|
208
234
|
process.stdout.write(delta)
|
|
209
235
|
}
|
|
210
236
|
}
|
|
211
|
-
|
|
212
|
-
// Self-emitted stream events are unused for a pure observer.
|
|
213
|
-
async onInternalEvent(): Promise<void> {}
|
|
214
237
|
}
|
|
215
238
|
```
|
|
216
239
|
|
|
217
|
-
Enable streaming
|
|
240
|
+
Enable streaming by passing `streaming: true` to `runInference`:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
runInference({ model: "gpt-5.4", context, caller: this, environment, streaming: true })
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Requesting streaming for a model whose specification has `supportsStreaming: false` fails request validation before the API is called.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Structured output
|
|
251
|
+
|
|
252
|
+
When you need the model to respond with a specific JSON shape instead of free-form text, use structured output. Pass a `structuredOutput` (a `StructuredOutputFormat`) on the `runInference` params and the provider will enforce the JSON Schema:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
import { runInference } from "@mozaik-ai/core"
|
|
256
|
+
|
|
257
|
+
runInference({
|
|
258
|
+
model: "gpt-5.4",
|
|
259
|
+
context,
|
|
260
|
+
caller: this,
|
|
261
|
+
environment,
|
|
262
|
+
structuredOutput: {
|
|
263
|
+
name: "weather",
|
|
264
|
+
schema: {
|
|
265
|
+
type: "object",
|
|
266
|
+
properties: {
|
|
267
|
+
city: { type: "string" },
|
|
268
|
+
temperature: { type: "number" },
|
|
269
|
+
condition: { type: "string" },
|
|
270
|
+
},
|
|
271
|
+
required: ["city", "temperature", "condition"],
|
|
272
|
+
additionalProperties: false,
|
|
273
|
+
},
|
|
274
|
+
strict: true,
|
|
275
|
+
},
|
|
276
|
+
})
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
The response comes back as a `ModelMessageItem` with valid JSON in the text field — no new item type, consistent with OpenResponses.
|
|
280
|
+
|
|
281
|
+
Structured output works alongside tools and streaming. When streaming is enabled, partial JSON chunks arrive as `SemanticEvent`s and the final event contains the complete response.
|
|
282
|
+
|
|
283
|
+
To return to free-form text, simply omit `structuredOutput` on the next `runInference` call.
|
|
284
|
+
|
|
285
|
+
### Provider support
|
|
286
|
+
|
|
287
|
+
| Provider | Models | Strict schema enforcement |
|
|
288
|
+
| --------- | --------------------------------------------------------------------- | ------------------------------------------------------ |
|
|
289
|
+
| OpenAI | gpt-5.4, gpt-5.4-mini, gpt-5.4-nano, gpt-5.5 | Yes |
|
|
290
|
+
| Anthropic | claude-opus-4-7, claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5 | Yes |
|
|
291
|
+
| Gemini | gemini-3.1-pro-preview, gemini-3.5-flash | Yes |
|
|
292
|
+
| DeepSeek | deepseek-v4-flash, deepseek-v4-pro | Not supported — use prompt-based JSON guidance instead |
|
|
293
|
+
|
|
294
|
+
Requesting structured output for a model whose specification has `supportsStructuredOutput: false` fails request validation before the API call.
|
|
218
295
|
|
|
219
296
|
---
|
|
220
297
|
|
|
@@ -223,24 +300,24 @@ Enable streaming on the model before calling `runInference` as usual. `setStream
|
|
|
223
300
|
Every participant receives lifecycle notifications when it or others join/leave an environment:
|
|
224
301
|
|
|
225
302
|
```ts
|
|
226
|
-
export class TeamAgent extends
|
|
303
|
+
export class TeamAgent extends BaseParticipant {
|
|
227
304
|
// Called when this participant joins an environment.
|
|
228
|
-
onJoined(
|
|
305
|
+
onJoined(): void {
|
|
229
306
|
console.log("I joined the environment")
|
|
230
307
|
}
|
|
231
308
|
|
|
232
309
|
// Called when this participant leaves an environment.
|
|
233
|
-
onLeft(
|
|
310
|
+
onLeft(): void {
|
|
234
311
|
console.log("I left the environment")
|
|
235
312
|
}
|
|
236
313
|
|
|
237
314
|
// Called when another participant joins the same environment.
|
|
238
|
-
onParticipantJoined(participant: Participant
|
|
315
|
+
onParticipantJoined(participant: Participant): void {
|
|
239
316
|
console.log(`${participant.constructor.name} joined`)
|
|
240
317
|
}
|
|
241
318
|
|
|
242
319
|
// Called when another participant leaves the same environment.
|
|
243
|
-
onParticipantLeft(participant: Participant
|
|
320
|
+
onParticipantLeft(participant: Participant): void {
|
|
244
321
|
console.log(`${participant.constructor.name} left`)
|
|
245
322
|
}
|
|
246
323
|
}
|
|
@@ -254,39 +331,77 @@ This lets participants react to membership changes — for example, an agent cou
|
|
|
254
331
|
|
|
255
332
|
Participants can listen to external events and react by overriding methods like `onMessage`, `onExternalFunctionCall`, `onExternalFunctionCallOutput`, `onExternalReasoning`, and `onExternalModelMessage`.
|
|
256
333
|
|
|
334
|
+
### Selective listening
|
|
335
|
+
|
|
336
|
+
By default a participant reacts to events from every other participant. To scope a participant so it only reacts to specific participant _types_, populate its `listens` list with those classes. When `listens` is non-empty, the environment only delivers external events whose source is an instance of one of the listed classes:
|
|
337
|
+
|
|
338
|
+
```ts
|
|
339
|
+
import { BaseParticipant } from "@mozaik-ai/core"
|
|
340
|
+
|
|
341
|
+
export class Critic extends BaseParticipant {
|
|
342
|
+
// Only react to events produced by Writer participants.
|
|
343
|
+
protected listens = [Writer]
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Error handling
|
|
350
|
+
|
|
351
|
+
When any handler throws, the environment catches it and routes it as an `AgenticError` instead of crashing the run. The participant whose handler threw receives `onError(error)`; every other participant receives `onParticipantError(source, error)`. After its own `onError`, the failing participant is marked inactive in that environment so it stops receiving further events.
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
import { BaseParticipant, Participant, AgenticError } from "@mozaik-ai/core"
|
|
355
|
+
|
|
356
|
+
export class ResilientAgent extends BaseParticipant {
|
|
357
|
+
onError(error: AgenticError): void {
|
|
358
|
+
console.error("my handler threw:", error.message)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
onParticipantError(source: Participant, error: AgenticError): void {
|
|
362
|
+
console.warn(`${source.constructor.name} failed:`, error.message)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
`AgenticError` carries the originating participant (`getSource()`) and environment (`getEnvironment()`).
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
257
371
|
## Passive observer
|
|
258
372
|
|
|
259
|
-
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 `
|
|
373
|
+
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 `BaseParticipant` and override only the handlers you care about — everything else stays a no-op:
|
|
260
374
|
|
|
261
375
|
```ts
|
|
262
|
-
import {
|
|
376
|
+
import {
|
|
377
|
+
BaseParticipant,
|
|
378
|
+
Participant,
|
|
379
|
+
FunctionCallItem,
|
|
380
|
+
FunctionCallOutputItem,
|
|
381
|
+
ReasoningItem,
|
|
382
|
+
ModelMessageItem,
|
|
383
|
+
} from "@mozaik-ai/core"
|
|
263
384
|
|
|
264
|
-
export class TranscriptLogger extends
|
|
385
|
+
export class TranscriptLogger extends BaseParticipant {
|
|
265
386
|
async onMessage(message: string): Promise<void> {
|
|
266
387
|
console.log("[message]", message)
|
|
267
388
|
}
|
|
268
389
|
|
|
269
390
|
async onExternalFunctionCall(source: Participant, item: FunctionCallItem): Promise<void> {
|
|
270
|
-
console.log(`[${source.constructor.name}] function_call`, item
|
|
391
|
+
console.log(`[${source.constructor.name}] function_call`, item)
|
|
271
392
|
}
|
|
272
393
|
|
|
273
394
|
async onExternalFunctionCallOutput(source: Participant, item: FunctionCallOutputItem): Promise<void> {
|
|
274
|
-
console.log(`[${source.constructor.name}] function_call_output`, item
|
|
395
|
+
console.log(`[${source.constructor.name}] function_call_output`, item)
|
|
275
396
|
}
|
|
276
397
|
|
|
277
398
|
async onExternalReasoning(source: Participant, item: ReasoningItem): Promise<void> {
|
|
278
|
-
console.log(`[${source.constructor.name}] reasoning`, item
|
|
399
|
+
console.log(`[${source.constructor.name}] reasoning`, item)
|
|
279
400
|
}
|
|
280
401
|
|
|
281
402
|
async onExternalModelMessage(source: Participant, item: ModelMessageItem): Promise<void> {
|
|
282
|
-
console.log(`[${source.constructor.name}] model_message`, item
|
|
403
|
+
console.log(`[${source.constructor.name}] model_message`, item)
|
|
283
404
|
}
|
|
284
|
-
|
|
285
|
-
// Self-emitted handlers (onFunctionCall, onReasoning, …) can be no-ops for a pure observer.
|
|
286
|
-
async onFunctionCall(): Promise<void> {}
|
|
287
|
-
async onFunctionCallOutput(): Promise<void> {}
|
|
288
|
-
async onReasoning(): Promise<void> {}
|
|
289
|
-
async onModelMessage(): Promise<void> {}
|
|
290
405
|
}
|
|
291
406
|
```
|
|
292
407
|
|
|
@@ -294,7 +409,7 @@ export class TranscriptLogger extends Participant {
|
|
|
294
409
|
|
|
295
410
|
## Context and models (reference)
|
|
296
411
|
|
|
297
|
-
`ModelContext` is the ordered list of `ContextItem`s a
|
|
412
|
+
`ModelContext` is the ordered list of `ContextItem`s a model is asked to reason over. It is constructed and mutated explicitly — typically inside a participant in response to delivered items.
|
|
298
413
|
|
|
299
414
|
```ts
|
|
300
415
|
import { ModelContext, DeveloperMessageItem, UserMessageItem, InMemoryModelContextRepository } from "@mozaik-ai/core"
|
|
@@ -309,55 +424,47 @@ await repo.save(context)
|
|
|
309
424
|
|
|
310
425
|
Implement `ModelContextRepository` to plug in any storage backend.
|
|
311
426
|
|
|
312
|
-
|
|
427
|
+
A model is selected by its `ModelName` string. Mozaik resolves the name to a provider `Endpoint` and a `ModelSpecification` internally, maps the `ModelContext` to that provider's API, and returns typed `ContextItem`s (and `SemanticEvent`s when streaming). Bundled model names:
|
|
428
|
+
|
|
429
|
+
| Provider | `ModelName` values |
|
|
430
|
+
| --------- | ------------------------------------------------------------------------------------- |
|
|
431
|
+
| OpenAI | `"gpt-5.4"`, `"gpt-5.4-mini"`, `"gpt-5.4-nano"`, `"gpt-5.5"` |
|
|
432
|
+
| Anthropic | `"claude-haiku-4-5"`, `"claude-sonnet-4-6"`, `"claude-opus-4-7"`, `"claude-opus-4-8"` |
|
|
433
|
+
| Gemini | `"gemini-3.5-flash"`, `"gemini-3.1-pro-preview"` |
|
|
434
|
+
| DeepSeek | `"deepseek-v4-flash"`, `"deepseek-v4-pro"` |
|
|
435
|
+
|
|
436
|
+
You drive inference with the `runInference` capability; it streams the resulting items into the environment for participants to react to:
|
|
313
437
|
|
|
314
438
|
```ts
|
|
315
|
-
import {
|
|
439
|
+
import { runInference, ModelContext } from "@mozaik-ai/core"
|
|
316
440
|
|
|
317
|
-
const runner = new OpenAIInferenceRunner()
|
|
318
441
|
const context = ModelContext.create("demo")
|
|
319
442
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
443
|
+
runInference({ model: "gpt-5.4", context, caller: this, environment })
|
|
444
|
+
// → environment delivers ReasoningItem | FunctionCallItem | ModelMessageItem (and SemanticEvent when streaming)
|
|
323
445
|
```
|
|
324
446
|
|
|
325
447
|
---
|
|
326
448
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
Mozaik uses async generators for inference and function calls — that's what lets the system emit events incrementally so participants can react to them as they arrive. Swap any runner to change _how_ events are produced.
|
|
330
|
-
|
|
331
|
-
Humans send text with `sendMessage(environment, message)`; other participants receive it via `onMessage`.
|
|
449
|
+
### Tools
|
|
332
450
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
An `InferenceRunner` can yield `ReasoningItem`, `FunctionCallItem`, `ModelMessageItem`, and `SemanticEvent` (when streaming).
|
|
451
|
+
A `Tool` is a function declaration with its own executor: `name`, `description`, JSON Schema `parameters`, `strict`, and an `invoke(args)` that runs the call. Pass tools on the `runInference` params; when the model emits a `FunctionCallItem`, run it with `executeFunctionCall`, which calls the matching tool's `invoke` and emits a `FunctionCallOutputItem`.
|
|
336
452
|
|
|
337
453
|
```ts
|
|
338
|
-
import {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
constructor(private readonly tools: Tool[]) {}
|
|
353
|
-
|
|
354
|
-
async *run(call: FunctionCallItem, signal?: AbortSignal): AsyncIterable<FunctionCallOutputItem> {
|
|
355
|
-
const tool = this.tools.find((t) => t.name === call.name)
|
|
356
|
-
if (!tool) throw new Error(`Unknown tool: ${call.name}`)
|
|
357
|
-
|
|
358
|
-
const result = await tool.invoke(JSON.parse(call.args))
|
|
359
|
-
yield FunctionCallOutputItem.create(call.callId, JSON.stringify(result))
|
|
360
|
-
}
|
|
454
|
+
import { Tool } from "@mozaik-ai/core"
|
|
455
|
+
|
|
456
|
+
const getWeather: Tool = {
|
|
457
|
+
type: "function",
|
|
458
|
+
name: "get_weather",
|
|
459
|
+
description: "Get the current weather for a city",
|
|
460
|
+
parameters: {
|
|
461
|
+
type: "object",
|
|
462
|
+
properties: { city: { type: "string" } },
|
|
463
|
+
required: ["city"],
|
|
464
|
+
additionalProperties: false,
|
|
465
|
+
},
|
|
466
|
+
strict: true,
|
|
467
|
+
invoke: async ({ city }: { city: string }) => ({ city, temperature: 21, condition: "sunny" }),
|
|
361
468
|
}
|
|
362
469
|
```
|
|
363
470
|
|