brime

Stream research to a frontend

Pipe Brime SSE events into a React or vanilla-JS UI.

Brime's deep research SSE streams agent events as they happen. Forward them to your frontend so users see live progress instead of staring at a spinner.

Backend (Node.js, Hono)

import { Hono } from "hono";
import { Brime } from "@brime-ai/brime";

const app = new Hono();

app.post("/research", async (c) => {
  const { query } = await c.req.json();
  const client = new Brime();

  // Forward Brime SSE directly to the browser.
  return c.body(
    new ReadableStream({
      async start(ctrl) {
        const enc = new TextEncoder();
        try {
          for await (const evt of client.researchStream({ query })) {
            ctrl.enqueue(enc.encode(`event: ${evt.event}\ndata: ${JSON.stringify(evt.data)}\n\n`));
            if (evt.event === "complete" || evt.event === "error" || evt.event === "timeout") break;
          }
        } finally {
          ctrl.close();
        }
      },
    }),
    { headers: { "content-type": "text/event-stream" } },
  );
});

Frontend (vanilla EventSource)

const es = new EventSource("/research", { withCredentials: false });

es.addEventListener("tool_call", (e) => {
  const { round, queries } = JSON.parse(e.data);
  console.log(`Round ${round}: searching for`, queries);
});

es.addEventListener("complete", (e) => {
  const { answer, sources } = JSON.parse(e.data);
  document.querySelector("#answer").textContent = answer;
  es.close();
});

Frontend (React)

import { useEffect, useState } from "react";

export function ResearchPanel({ query }: { query: string }) {
  const [events, setEvents] = useState<string[]>([]);
  const [answer, setAnswer] = useState("");

  useEffect(() => {
    const es = new EventSource(`/research?q=${encodeURIComponent(query)}`);
    es.addEventListener("status", (e) => setEvents((es) => [...es, JSON.parse(e.data)]));
    es.addEventListener("complete", (e) => {
      setAnswer(JSON.parse(e.data).answer);
      es.close();
    });
    return () => es.close();
  }, [query]);

  return (
    <div>
      <ul>{events.map((s, i) => <li key={i}>{s}</li>)}</ul>
      {answer && <article>{answer}</article>}
    </div>
  );
}

This recipe will be expanded with Last-Event-ID resume on disconnect and a full reconnection helper.