Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.m4trix.dev/llms.txt

Use this file to discover all available pages before exploring further.

The IO layer turns an AgentNetwork into an HTTP API. Built-in adapters: NextEndpoint (Next.js) and ExpressEndpoint (Express).

Exposing a Network

const api = network.expose({
  protocol: 'sse',
  select: { channels: 'client' },
  startEventName: 'user-request',
});

expose() Options

OptionDescription
protocol'sse' (currently the only option)
authPer-request auth callback: async (req) => { allowed, message?, status? }
select.channelsChannel(s) to stream from (string or string[])
select.eventsFilter to specific event names (string[])
startEventNameEvent name published when request arrives (default: 'request')
onRequestCallback before streaming; must call emitStartEvent() if provided
planeOptional: reuse existing EventPlane

NextEndpoint

import { NextEndpoint } from '@m4trix/core/matrix';

const handler = NextEndpoint.from(api).handler();
export const GET = handler;
export const POST = handler;
Maps ExposedAPI to Next.js App Router handlers. Handles Request, auth, payload extraction, SSE response.

ExpressEndpoint

import { ExpressEndpoint } from '@m4trix/core/matrix';

const handler = ExpressEndpoint.from(api).handler();
app.get('/api/stream', handler);
app.post('/api/stream', handler);
Requires express.json() (or body-parser) for POST. Handles client disconnect and res.flush() when available.

Response Format

Events are streamed as SSE:
event: agent-response
data: {"name":"agent-response","meta":{"runId":"..."},"payload":{"text":"Hello!"}}

Full Example: Next.js Streaming API

// app/api/chat/route.ts
import OpenAI from 'openai';
import {
  AgentFactory,
  AgentNetwork,
  AgentNetworkEvent,
  NextEndpoint,
  S,
} from '@m4trix/core/matrix';

const chatRequest = AgentNetworkEvent.of('chat-request', S.Struct({ message: S.String }));
const chatResponse = AgentNetworkEvent.of('chat-response', S.Struct({ text: S.String, done: S.Boolean }));

const chatAgent = AgentFactory.run()
  .listensTo([chatRequest])
  .emits([chatResponse])
  .logic(async ({ triggerEvent, emit }) => {
    const openai = new OpenAI();
    const stream = await openai.chat.completions.create({
      model: 'gpt-4o',
      stream: true,
      messages: [{ role: 'user', content: triggerEvent.payload.message }],
    });
    for await (const chunk of stream) {
      const text = chunk.choices[0]?.delta?.content;
      if (text) emit({ name: 'chat-response', payload: { text, done: false } });
    }
    emit({ name: 'chat-response', payload: { text: '', done: true } });
  })
  .produce({});

const network = AgentNetwork.setup(
  ({ mainChannel, createChannel, sink, registerAgent }) => {
    const main = mainChannel('main');
    const client = createChannel('client').sink(sink.httpStream());
    registerAgent(chatAgent).subscribe(main).publishTo(client);
  },
);

const api = network.expose({
  protocol: 'sse',
  select: { channels: 'client' },
  startEventName: 'chat-request',
});

const handler = NextEndpoint.from(api).handler();
export const GET = handler;
export const POST = handler;

See Also